2016-07-15 5 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 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

깔끔하네요! :) –

관련 문제