如何最好地防止在完成之前再次运行异步方法?

时间:2017-05-09 01:17:16

标签: c# asynchronous async-await

我已经有了这种模式,可以防止在之前有机会完成之前调用异步方法。

我的解决方案涉及需要一个标志,然后需要锁定旗帜,感觉非常冗长。是否有更自然的方法来实现这一目标?

public class MyClass
{
    private object SyncIsFooRunning = new object();
    private bool IsFooRunning { get; set;}

    public async Task FooAsync()
    {
        try
        {
            lock(SyncIsFooRunning)
            {
                if(IsFooRunning)
                    return;

                IsFooRunning = true;
            }

            // Use a semaphore to enforce maximum number of Tasks which are able to run concurrently.
            var semaphoreSlim = new SemaphoreSlim(5);
            var trackedTasks = new List<Task>();

            for(int i = 0; i < 100; i++)
            {
                await semaphoreSlim.WaitAsync();

                trackedTasks.Add(Task.Run(() =>
                {
                    // DoTask();
                    semaphoreSlim.Release();
                }));
            }

            // Using await makes try/catch/finally possible.
            await Task.WhenAll(trackedTasks);
        }
        finally
        {
            lock(SyncIsFooRunning)
            {
                IsFooRunning = false;
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

如评论中所述,如果您愿意,可以使用function invokeActionProcessor(functionName, message) { const payload = { operation: PROCESS_MESSAGE, message, }; const params = { FunctionName: functionName, InvocationType: 'Event', Payload: new Buffer(JSON.stringify(payload)), }; return new Promise((resolve, reject) => { console.log("invokeActionProcessor: invoking the function " + functionName); try { Lambda.invoke(params, function(err, data) { if(err) { console.log("invokeActionProcessor: got error: " + err); reject(err); } else { console.log("invokeActionProcessor: success, " + data); resolve(null, data); } }); } catch (err) { console.log("invokeActionProcessor: got exception - " + err); } }); }

Interlocked.CompareExchange()

那就是说,我觉得这太过分了。在此类场景中使用public class MyClass { private int _flag; public async Task FooAsync() { try { if (Interlocked.CompareExchange(ref _flag, 1, 0) == 1) { return; } // do stuff } finally { Interlocked.Exchange(ref _flag, 0); } } } 没有任何问题,特别是如果您不希望在该方法上进行大量争用。我认为更好的方法是包装方法,以便调用者始终可以lock结果,无论是否启动了新的异步操作:

await

最后,在评论中,你这样说:

  

似乎有点奇怪,所有返回类型仍然对任务有效?

我不清楚你的意思。在您的方法中,唯一有效的返回类型是public class MyClass { private readonly object _lock = new object(); private Task _task; public Task FooAsync() { lock (_lock) { return _task != null ? _task : (_task = FooAsyncImpl()); } } public async Task FooAsyncImpl() { try { // do async stuff } finally { lock (_lock) _task = null; } } } void。如果您的Task语句返回了实际值,则必须使用return,其中Task<T>是return语句返回的类型。