[英]How to make different calls to an async method waiting for the result of first call?
假設我有一個從服務器獲取數據的方法
Task<Result> GetDataFromServerAsync(...)
如果正在進行正在進行的呼叫,我不想向服務器發起新請求,而是等待原始呼叫完成。
讓我說我有
var result = await objet.GetDataFromServerAsync(...);
在一個不同的地方,幾乎在同一時間召喚我有第二個電話
var result2 = await objet.GetDataFromServerAsync(...);
如果第一個沒有完成,我不希望第二個向服務器發起新請求。 我希望兩個電話在第一次通話完成后立即獲得相同的結果。 這是一個概念證明,我有選擇,但我想看看這樣做有多容易。
以下是使用Lazy<Task<T>>
的快速示例:
var lazyGetDataFromServer = new Lazy<Task<Result>>
(() => objet.GetDataFromServerAsync(...));
var result = await lazyGetDataFromServer.Value;
var result2 = await lazyGetDataFromServer.Value;
由於Lazy<T>
是線程安全的,因此這兩個await
s是由單獨的線程完成並不重要,所以如果第二次運行則result2
仍將等待並使用result
的相同輸出。
使用此處的代碼,您可以將其包裝在名為AsyncLazy<T>
的類中,並添加一個自定義GetAwaiter
以便您可以await
它而無需執行.Value
,非常整潔=)
您可以在方法中使用任何進行同步的方法。 例如,我使用了SemaphoreSlim
:
public class PartyMaker
{
private bool _isProcessing;
private readonly SemaphoreSlim _slowStuffSemaphore = new SemaphoreSlim(1, 1);
private DateTime _something;
public async Task<DateTime> ShakeItAsync()
{
try
{
var needNewRequest = !_isProcessing;
await _slowStuffSemaphore.WaitAsync().ConfigureAwait(false);
if (!needNewRequest) return _something;
_isProcessing = true;
_something = await ShakeItSlowlyAsync().ConfigureAwait(false);
return _something;
}
finally
{
_isProcessing = false;
_slowStuffSemaphore.Release();
}
}
private async Task<DateTime> ShakeItSlowlyAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
return DateTime.UtcNow;
}
}
用法:
var maker = new PartyMaker();
var tasks = new[] {maker.ShakeItAsync(), maker.ShakeItAsync()};
Task.WaitAll(tasks);
foreach (var task in tasks)
{
Console.WriteLine(task.Result);
}
Console.WriteLine(maker.ShakeItAsync().Result);
結果如下:
17.01.2017 22:28:39
17.01.2017 22:28:39
17.01.2017 22:28:41
UPD通過此修改,您可以使用args調用異步方法:
public class PartyMaker
{
private readonly SemaphoreSlim _slowStuffSemaphore = new SemaphoreSlim(1, 1);
private readonly ConcurrentDictionary<int, int> _requestCounts = new ConcurrentDictionary<int, int>();
private readonly ConcurrentDictionary<int, DateTime> _cache = new ConcurrentDictionary<int, DateTime>();
public async Task<DateTime> ShakeItAsync(Argument argument)
{
var key = argument.GetHashCode();
DateTime result;
try
{
if (!_requestCounts.ContainsKey(key))
{
_requestCounts[key] = 1;
}
else
{
++_requestCounts[key];
}
var needNewRequest = _requestCounts[key] == 1;
await _slowStuffSemaphore.WaitAsync().ConfigureAwait(false);
if (!needNewRequest)
{
_cache.TryGetValue(key, out result);
return result;
}
_cache.TryAdd(key, default(DateTime));
result = await ShakeItSlowlyAsync().ConfigureAwait(false);
_cache[key] = result;
return result;
}
finally
{
_requestCounts[key]--;
if (_requestCounts[key] == 0)
{
int temp;
_requestCounts.TryRemove(key, out temp);
_cache.TryRemove(key, out result);
}
_slowStuffSemaphore.Release();
}
}
private async Task<DateTime> ShakeItSlowlyAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
return DateTime.UtcNow;
}
}
public class Argument
{
public Argument(int value)
{
Value = value;
}
public int Value { get; }
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.