2017-02-18 2 views
0

"C# in a Nutshell"의 비동기 함수 섹션을 읽었습니다. 예를 들면 다음과 같습니다 :캐시 비동기 함수 결과가 C#

'다운로드'버튼 클릭 이벤트를 처리해야한다고 가정하고 다운로드 결과를 캐싱하려고합니다. 웹 사이트를 다운로드하는 데 신경 쓰지 않으면 버튼을 반복해서 클릭하면 eaiser가 표시됩니다.

private static Dictionary<string, string> _cache = new Dictionary<string, string>(); 
    async Task<string> DownloadAsync(string uri) 
    { 
     string content; 
     if (_cache.TryGetValue(uri, out content)) return content; 
     return _cache[uri] = await new WebClient().DownloadStringTaskAsync(uri); 
    } 

하지만 예를 들어 두 번 연속 클릭하면 동일한 웹 사이트로 중복 다운로드가 실행됩니다. 이를 보호하기 위해이 책에서는 Task<string> 캐싱을 제안합니다.

private static Dictionary<string, Task<string>> _futureCache = new Dictionary<string, Task<string>>(); 
Task<string> GetWebPage(string uri) 
    { 
     lock (_futureCache) 
     { 
      Task<string> task; 
      if (_futureCache.TryGetValue(uri, out task)) return task; 
      return _futureCache[uri] = new WebClient().DownloadStringTaskAsync(uri); 
     } 
    } 

그러나이 보호 기능이 어떻게 효과적 일 수 있는지 이해하지 못합니다. 내가 클릭 이벤트 처리기는이 같은 것입니다 가정

_downloadBtn.Click += async (sender, args) => 
     { 
      var uri = "http://www.bbc.co.uk"; // for argument sake, it always downloads bbc... 
      string result = await GetWebPage(uri); 
      // process the result... 
     }; 

어떤 이유로, 첫 번째 클릭에 의해 트리거 다운로드가 진행 여전히 후 버튼이 2 시간 동안 클릭, 경우; 아직 두 번째 클릭이 시작될 때까지 캐시가 첫 번째 다운로드에서 채워지지 않았으므로 페이지를 두 번 다운로드하지 않습니까? 내 이해가 잘못 되었다면 그 이유를 설명하십시오. 그렇지 않으면 반복되는 사용자 클릭을 방지하는 캐시를 구현하는 방법은 무엇입니까?

그렇다면 캐시가 이벤트 핸들러 (UI) 컨텍스트 외부에서 사용되는 경우 효과적으로 작동한다는 것을 알고 있습니다. 그 아래에 한 번만 다운로드됩니다.

async void Foo() 
    { 
     var uri = "http://www.bbc.co.uk"; 
     string result; 
     for (int i = 0; i < 2; i++) //Stimulate repeated call 
     { 
      result = await GetWebPage(uri); 
      Console.WriteLine(result.Length); 
     } 
    } 
+1

코드가 정상적으로 작동합니다. 힌트 :'GetWebPage'에는'await'이 없습니다 :) –

+0

@LucasTrzesniewski 그것은 의도적으로 동시성을 유발할 것을 기다리지 않고 있습니다! – stt106

+0

바로 코드가 이미 진행중인 작업 (또는 해당 문제에 대해 이미 완료된 상태) 인 경우 두 번째 작업을 추가하지 않습니다 :) –

답변

1

_futureCache는 사전 uri 키에 기초 Task 캐싱된다. 웹 페이지를 요청할 때 새로운 Task을 만들지 못하도록 막고, 대신 Task이 완료되었는지 여부를 묻는 질문에 uri이 이미 마지막으로 작성된 Task을 반환합니다. 그런 다음 호출 코드는 해당 uri에 대한 모든 요청에 ​​대해 동일한 Task을 기다립니다. 이미 완료 되었으면 즉시 Task의 결과와 함께 반환됩니다. 그렇지 않은 경우 완료 될 때까지 기다립니다. 무슨 일이 있어도 한 번만 페이지를 가져옵니다.

+0

아마도 당신을 이해하지 못 하겠지만 uri에 대한 첫 번째 호출에서 캐시가 없으면이 다운로드가 진행되는 동안 캐시가 다운로드를 요청하고 캐시가 진행중인 작업에 이미 채워 졌다고 말하는 것입니까? – stt106

+0

'GetWebPage'는'async'로 표시되지 않습니다. 작업을 반환하지만 작업이 완료 될 때까지 기다리지 않습니다. 따라서 캐시에서 '_futureCache'에 대한 모니터 잠금이 유지되는 동안 경쟁 조건을 방지합니다. 두 번째 요청은 작업이 * 시작 *되고 캐시에 추가 될 때까지 몇 나노초 동안 차단됩니다. – Tim

+0

좋아, 첫 번째 작업이 시작되자 마자 첫 번째 댓글과 동일한 의미가 있다고 생각합니다. 아직 진행 중이지만 계속 진행 중입니다. 따라서 두 번째 요청은 캐시에서 불완전한 작업을 얻지 만 여전히 다른 요청/작업을 트리거하지는 않습니까? – stt106