2014-06-12 39 views
4

我有很多這樣的代碼:如何將此代碼轉換爲異步等待?

 var feed = new DataFeed(host, port); 
     feed.OnConnected += (conn) => 
     { 
      feed.BeginLogin(user, pass); 
     }; 
     feed.OnReady += (f) => 
     { 
      //Now I'm ready to do stuff. 
     }; 

     feed.BeginConnect(); 

正如你所看到的,我用做異步操作的常用方式。如何更改此代碼以使用async await?最好是這樣的:

public async void InitConnection() 
{ 
    await feed.BeginConnect(); 
    await feed.BeginLogin(user, pass); 

    //Now I'm ready 
} 
+0

BeginXXX操作是否返回IAsyncResult? –

+0

@SergeyBerezovskiy:例如'BeginConnect'返回void。它在內部調用'Socket.BeginConnect'並等待調用'Socket.EndConnect'。一旦被調用,它就會觸發OnConnected事件。 – nakiya

+2

那種破壞它的異步目的,不是嗎?與僅僅調用'Connect'完全不同? – Luaan

回答

9

您可以使用TaskCompletionSource<T>將您的EAP(基於事件的異步模式)封裝到任務中。目前還不清楚如何處理錯誤,並在數據傳送專線類取消操作,那麼您就需要修改這個代碼,並添加錯誤處理(sample):

private Task ConnectAsync(DataFeed feed) 
{ 
    var tcs = new TaskCompletionSource<object>(); 
    feed.OnConnected += _ => tcs.TrySetResult(null); 
    feed.BeginConnect(); 
    return tcs.Task; 
} 

private Task LoginAsync(DataFeed feed, string user, string password) 
{ 
    var tcs = new TaskCompletionSource<object>(); 
    feed.OnReady += _ => tcs.TrySetResult(null); 
    feed.BeginLogin(user, pass); 
    return tcs.Task; 
} 

現在你可以使用以下方法:

public async void InitConnection() 
{ 
    var feed = new DataFeed(host, port); 
    await ConnectAsync(feed); 
    await LoadAsync(feed, user, pass); 
    //Now I'm ready 
} 

注 - 您可以將這些異步方法移至DataFeed類。但是,如果您可以修改DataFeed,那麼最好使用TaskFactory.FromAsyncAPM API包裝爲任務。

不幸的是,沒有非通用的TaskCompletionSource這將返回非通用的Task所以,通常解決方法是使用Task<object>

+0

我在您的方法中看不到'async'關鍵字。不應該有一個? – nakiya

+2

+1,這是'TaskCompletetionSource '的用途。一個問題,失敗會發生什麼?出於興趣,使用「TaskCompletetionSource 」與「TaskCompletetionSource 」有什麼好處,我懷疑它沒有什麼區別? – Jodrell

+0

@nakiya不,他們不會「等待」任何事情。任何返回「Task」的函數都是「可等待的」。 – Jodrell

0

你需要改變你的DataFeed類來支持。你只需要在那裏使用任務異步模式。這意味着DataFeed中的所有異步方法都必須返回Task(或某些Task<T>),並且它們應該被命名爲ConnectAsync(例如)。

現在,與Socket,這不是完全容易,因爲Socket上的XXXAsync方法實際上並不等待!最簡單的方法就是使用TcpClientTcpListener respectivelly(前提是你使用的是TCP):

public async Task<bool> LoginAsync(TcpClient client, ...) 
{ 
    var stream = client.GetStream(); 

    await stream.WriteAsync(...); 

    // Make sure you read all that you need, and ideally no more. This is where 
    // TCP can get very tricky... 
    var bytesRead = await stream.ReadAsync(buf, ...); 

    return CheckTheLoginResponse(buf); 
} 

,然後只用它從外面:

await client.ConnectAsync(...); 

if (!(await LoginAsync(client, ...))) throw new UnauthorizedException(...); 

// We're logged in 

這只是示例代碼,我假設你實際上能夠寫出像樣的TCP代碼。如果你這樣做,使用await異步編寫它並不是很難。只要確保你總是在等待一些異步I/O操作。

如果你想只用Socket做同樣的,你可能會使用Task.FromAsync,它提供了繞着BeginXXX/EndXXX方法的包裝 - 非常方便。

+0

這並不理想,因爲即使在我收到登錄響應之前,我也會收到來自服務器的心跳。 – nakiya

+0

@nakiya好的,是的,這就是爲什麼你需要避免直接使用Write和Read。應該有一個處理消息的消息處理器,並且您應該等待。所以,而不是'await stream.ReadAsync',你會'等待messageProcessor.WaitForMessageAsync (...)'或其他東西。這完全取決於你想支持等待的程度 - 如果你想慢下來,像Sergey提議的那樣,在TaskCompletionSource中包裝事件是一個很好的方法。如果你不介意把所有事情都改變成等待模式,我的方式可能會更好。不過,它確實需要更多的管道。 – Luaan

+0

我如何去實現你提到的'WaitForMessageAsync ?我很困惑。你可以提供一個例子嗎? – nakiya

相關問題