2017-07-27 32 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