2016-07-15 4 views
2

私は、ApiClientクラスの複数のコンシューマによって同時に呼び出されるメソッドを持っています。最初のコールが完了してから残りのコールが短絡されるまで、メソッドへの同時呼び出しをブロックします。同時呼び出しをブロックし、非同期メソッドから単一の結果を返す

以下の擬似コードでは、複数のスレッドがRefreshApiTokenAsync()を呼び出すことがあります。私は内部のthis.GetNewApiTokenAsync()方法への単一の呼び出しを除いてすべてを防止したいと思う。これにより、コードが複数のスレッドで有効期限が切れたApiTokenを検出し、それを複数回リフレッシュしようとする状況が回避されます。

public class ApiClient 
{ 
    private static readonly ConcurrentDictionary<string, string> ApiTokens = new ConcurrentDictionary<string, string>(); 

    public async Task DoSomething() 
    { 
     // Call third party API and then detect an out of date API token. 
     // The CallThirdPartyApi uses the token in the ApiTokens ConcurrentDictionary 
     var result = await CallThirdPartyApi(); 

     if (result.ApiTokenOutOfDate) { 
      await this.RefreshApiTokenAsync(); 
      result = await CallThirdPartyApi();     
     } 

     return result; 
    } 

    private async Task<string> RefreshApiTokenAsync() 
    { 
     string newToken = await this.GetNewApiTokenAsync(); 
     return ApiTokens.AddOrUpdate("ApiToken", newToken, (key, value) => newToken); 
    } 
} 

私はこれがデバウンスと見なされると信じていますが、これを達成する方法がわかりません。

+0

ですね()' ... – Malk

+0

ロックが複数のスレッドを妨げますGetNewApiTokenAsyncを同時に呼び出すことはできませんが、最終的にはすべて呼び出されます。私が欲しいのは、GetNewApiTokenAsyncへの最初の呼び出しが行われることですが、実行中にメソッドを呼び出すための他の試みは、効果的に破棄されます。 –

+0

'if(locked)return else else lockAndRun()' – Malk

答えて

2

リフレッシュトークンタスクを保存して、発信者に返すことができます。トークンがリフレッシュされた後、呼び出し元は実行を続行できます。

private static readonly ConcurrentDictionary<string, Lazy<Task>> RefreshTokenTasks = new ConcurrentDictionary<string, Lazy<Task>>(); 

とトークンの方法は、このようなものになることができ、リフレッシュ:ここではサンプルタスク店だあなたは `ロックを記述しているよう

private Task RefreshApiTokenAsync() 
{ 
    return RefreshTokenTasks.GetOrAdd("refreshTokenTask", _ => new Lazy<Task>(async() => 
    { 
     try 
     { 
      string newToken = await this.GetNewApiTokenAsync(); 
      ApiTokens.AddOrUpdate("ApiToken", newToken, (key, value) => newToken); 
     } 
     finally 
     { 
      Lazy<Task> refreshTask; 
      RefreshTokenTasks.TryRemove("refreshTokenTask", out refreshTask); 
     } 
    }, LazyThreadSafetyMode.ExecutionAndPublication)).Value; 
} 
+0

'Lazy <>'を使用して、アクションが複数回実行されないようにしていますか?私は、同時辞書で使用される楽観的なロックを指しています。 –

+1

はい 'GetNewApiTokenAsync'が複数回実行されないようにするために' Lazy'が使用されました。 –

+0

これはきれいです! :) –

関連する問題