2017-07-27 35 views
1

嗨,我有文本框有文本更改事件。每次在文本框中插入一个字符时,都会触发文本更改事件。文本更改事件调用异步任务方法。以下是我的事件和异步任务方法。如何确保异步任务方法调用得到同步执行

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(TED)
+0

怎么比当前行为的不同期望的行为? –

+0

你使用的是什么框架?这是一个桌面应用程序或一个Web应用程序? – Michael

+0

aaah如果你想排队工作,使用队列来存储要完成的工作,然后按顺序处理它 – user230910

回答

-1

一个容易的选择,根据不同的情况可能是:

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

    }); 
} 
+0

对不起,上面的代码不适合我。其实我在这里面临的问题是。在我的问题提到的顺序,三个异步电话,1.GetStocks(T) 2.GetStocks(Te) 3.GetStocks(特德)。但是我得到结果的顺序是3,2,1。但我希望结果按以下顺序回来1,2,3 – Vinay

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中,我们使用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; 
    } 
} 
+0

'EnterAsync'内的'ContinueWith'很奇怪。将'newTcs'作为状态传递,只让代理忽略它?这感觉就像试图避免关闭中途放弃的局部变量。提醒我这个'AsyncLock'版本(除了这个实际使用状态):https://blogs.msdn.microsoft.com/pfxteam/2012/02/12/building-async-coordination-primitives-part-6- asynclock /) –

+0

@KirillShlenskiy你是对的。从旧的Silverlight项目获取代码,其中此参数不是可选的。将更新答案。 – Iqon