2017-05-09 50 views
3

我有这种模式,以防止在先前有机会完成之前调用异步方法。如何最好地防止在完成之前再次运行异步方法?

我的解决方案涉及需要一个标志,然后需要锁定国旗,感觉非常详细。是否有更自然的方式来实现这一点?

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; 
      } 
     } 
    } 
} 
+0

可以使用'Interlocked.Exchange()'来代替'lock()'。 – SLaks

+1

不要使用'async void'。 – SLaks

+0

@Slaks - Interlocked.Exchange不支持bools。我将代码更改为异步任务,但它不需要任何其他更改。似乎有点奇怪,所有返回类型仍然对任务有效? –

回答

2

正如在评论中指出,你可以使用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; 
     } 
    } 
} 

最后,在评论,你说这个:

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

不清楚你的意思。在你的方法中,唯一有效的返回类型是voidTask。如果您的return声明返回实际值,则必须使用Task<T>,其中T是返回语句返回的类型。

+0

你可以简化'return _task!= null? _task :(_task = FooAsyncImpl());''返回_task? (_task = FooAsyncImpl());'。 – svick

相关问题