如何确保同步执行异步任务方法调用

时间:2017-07-27 12:09:22

标签: c# multithreading async-await task task-parallel-library

嗨我有文本更改事件的文本框。每次在文本框中插入字符时,都会触发文本更改事件。 Text changed事件调用异步Task方法。下面是我的事件和异步任务方法。

stdio

问题 我怎样才能确保一个接一个地同步调用GetStocks方法。

示例 假设用户输入public Textbox_TextChangedEvent() { GetStocks(texboxText); } public async Task GetStocks(string texboxText) { IsBusy = true; await Task.Run(() => { CreateCollection(texboxText); }); IsBusy = false; } 作为输入文本。然后我希望异步调用一个接一个地完成。 即它应按以下顺序调用GetStocks,并按照以下顺序完成任务。

  1. GetStocks(T)
  2. GetStocks(Te)的
  3. GetStocks(TED)

2 个答案:

答案 0 :(得分:0)

为了解决这样的问题,我们使用了AsyncLock之前的项目。 AsyncLock将等到上一次锁定被释放。

AsyncLock首先看起来有点复杂,但我希望提供的用法示例能够说明其行为。

public class AsyncLock
{
   private TaskCompletionSource<object> _lastSection;

   public AsyncLock()
   {
      _lastSection = new TaskCompletionSource<object>();
      _lastSection.SetResult(null);
   }

   public class ReleaseLock : IDisposable
   {
      private readonly TaskCompletionSource<object> _tcs;

      public ReleaseLock(TaskCompletionSource<object> tcs)
      {
         _tcs = tcs;
      }

      public void Dispose()
      {
         _tcs.SetResult(null);
      }
   }

   /// <summary>
   /// Enters and locks a critical section as soon as the currently executing task has left the section.
   /// The critical section is locked until the returned <see cref="IDisposable"/> gets disposed.
   /// </summary>
   public Task<ReleaseLock> EnterAsync()
   {
      var newTcs = new TaskCompletionSource<object>();
      var toAwait = Interlocked.Exchange(ref _lastSection, newTcs);
      return toAwait.Task.ContinueWith((_) => new ReleaseLock(newTcs), TaskContinuationOptions.ExecuteSynchronously);
   }
}

然后,您可以使用await AsyncLock.EnterAsync()等待,直到释放任何先前的锁定。在EnterAsync我们使用Task将当前Task后的ContinueWith排队。这意味着await AsyncLock.EnterAsync()将在前一个完成后执行。

using (await _lock.EnterAsync())
{
    // ...
}   

以下是一个用法示例:

class Program
{
   private static readonly AsyncLock _lock = new AsyncLock();

   private static async Task Test(int i, Task toComplete)
   {
      using (await _lock.EnterAsync())
      {
         await toComplete;
         Console.WriteLine(i);
      }
   }

   public static void Main(string[] args)
   {
      var tcs1 = new TaskCompletionSource<object>();
      var tcs2 = new TaskCompletionSource<object>();

      Task.Run(async () =>
      {
         var t1 = Test(1, tcs1.Task); // start first task
         var t2 = Test(2, tcs2.Task); // start second task

         tcs2.SetResult(null); // finish second first
         tcs1.SetResult(null); // fiish last task

         await Task.WhenAll(t1, t2); // will print: 1 and then 2
      }).Wait();
   }
}

Test方法将首先进入Async锁定,然后等待任务toComplete,然后写入控制台。

我们开始执行两项Test任务(&#34; 1&#34; &#34; 2&#34; )并完成第二项任务toComplete首先。如果没有AsyncLock上一个示例,则打印:&#34; 2&#34; &#34; 1&#34; 。然而,使用AsyncLock,任务将按照启动顺序进行处理。

备注:最后一句话。这将实现您的处理顺序,但有时可能会很棘手。使用这样的锁很容易导致死锁,这些死锁很难解决,而且最初难以找到。 非常小心地使用锁

编辑:以下是您的问题的用法示例:

private readonly AsyncLock _lock = new AsyncLock();

public Textbox_TextChangedEvent()
{
    GetStocks(texboxText); // every call is now "queued" after the previous one
}

public async Task GetStocks(string texboxText)
{
    using(await _lock.EnterAsync())
    {
        IsBusy = true;
        await Task.Run(() => { CreateCollection(texboxText); });
        IsBusy = false;
    }
}

答案 1 :(得分:-1)

根据具体情况,一个简单的选择可能是:

public async Task GetStocks(string texboxText)
{
    Task.Run(() => { 
       IsBusy = true;
       CreateCollection(texboxText); 
       IsBusy = false;

    });
}