簡體   English   中英

如何對等待第一次調用結果的異步方法進行不同的調用?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM