【问题标题】:How to make sure async task method calls gets executed synchronously [duplicate]如何确保异步任务方法调用同步执行[重复]
【发布时间】:2017-07-27 12:09:22
【问题描述】:

您好,我有一个文本框,其中包含文本更改事件。每次在文本框中插入字符时,都会触发文本更改事件。文本更改事件调用异步任务方法。下面是我的事件和异步任务方法。

public Textbox_TextChangedEvent()
    {
        GetStocks(texboxText);
    }

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

问题 如何确保 GetStocks 方法一个接一个地被同步调用。

示例 假设用户输入了Ted 作为输入文本。然后我希望异步调用一个接一个地完成。 即它应该按以下顺序调用 GetStocks,并按以下顺序完成任务。

  1. GetStocks(T)
  2. GetStocks(Te)
  3. GetStocks(泰德)

【问题讨论】:

  • 期望行为与当前行为有何不同?
  • 你用的是什么框架?这是桌面应用还是网络应用?
  • aaah如果要排队工作,使用队列存储要完成的工作,然后按顺序处理
  • 我正在使用 .Net 框架和 WPF 应用程序。

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


【解决方案1】:

为了解决此类问题,我们使用了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 中,我们使用ContinueWith 在当前Task 之后排队下一个Task。这意味着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 任务(“1”“2”)并首先完成第二个 toComplete。如果没有 AsyncLock,前面的示例会打印:"2", "1"。但是,使用 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;
    }
}

【讨论】:

  • ContinueWith 里面的 EnterAsync 很奇怪。将newTcs 作为状态传递,只是让代表忽略它?这感觉就像是在尝试避免关闭中途放弃的局部变量。让我想起了这个AsyncLock 版本(除了这个实际使用状态):blogs.msdn.microsoft.com/pfxteam/2012/02/12/…
  • @KirillShlenskiy 你是对的。从旧 Silverlight 项目中获取代码,该参数不是可选的。将更新答案。
【解决方案2】:

一个简单的选择,视情况而定:

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

    });
}

【讨论】:

  • 对不起,上面的代码对我不起作用。实际上我在这里面临的问题是。按照我的问题中提到的顺序,进行了三个异步调用,1.GetStocks(T) 2.GetStocks(Te) 3.GetStocks(Ted)。但是我得到结果的顺序是 3,2,1。但我希望结果按照 1,2,3 的顺序返回
猜你喜欢
  • 2017-01-11
  • 2015-03-08
  • 1970-01-01
  • 1970-01-01
  • 2021-05-07
  • 1970-01-01
  • 2019-11-25
  • 2021-09-25
  • 2011-08-22
相关资源
最近更新 更多