2012-11-26 69 views
1

我正在實現我的應用程序的網絡層,即使用異步JSON-RPC協議。WinRT:阻止任務

爲了與服務器通信,我想做一個方法,將發送一個正確的請求,等到服務器發送響應,並返回它。所有與使用異步/等待關鍵字。

這是簡化的示例代碼:

字符串響應;

Task<string> SendRequest(string methodName, string methodParams) 
{ 
    string request = generateRequest(methodName, methodParams); 

    await Send(request); // this will send using DataWriter, and StreamSocket 

    // block Task until response arrives 

    return response; 
} 


async void ReceiveLoop() 
{ 
    while (true) 
    { 
      uint numStrBytes = await _reader.LoadAsync(BufferSize); 

      string msg = _reader.ReadString(numStrBytes); 

      response = msg; 

      // unblock previously blocked SendRequest 
     }   
    } 
} 

async void main() 
{ 
    RecieveLoop(); 
} 

async void SendButtonPressed() 
{ 
    string response = await SendRequest("Test method", "Test params"); 

    Debug.WriteLine("Response = " + response); 
} 

這種模式的主要問題是這種阻塞行爲。此操作應阻止當前任務,並允許處理超時。 我試過使用ManualResetEvent和WaitOne(int)來處理這個問題,但它凍結了整個Thread,並且因爲我只使用async/await,所以它凍結了整個應用程序(UI Thread對我更精確)。

解決方案,對我來說看起來很hacky是我可以使用Task.Delay與CancellationTokens。

它看起來像這樣:

... 
CancellationTokenSource cts; 
int timeout = 10000; 

Task<string> SendRequest(string methodName, string methodParams) 
{ 
    ... (prepare request, and send) 

    cts = new CancellationTokenSource(); 

    try 
    { 
     await Task.Delay(timeout, cts.Token); 
    } catch(TaskCanceledException) 
    { 

    } 

    // do rest 
} 

async void ReceiveLoop() 
{ 
    // init recieve loop, and recieve message 

    cts.Cancel();  
} 

與解決方案(除了它看起來像一個黑客)是性能問題 - 每一個請求有拋出EXCETION,需要處理(在這種情況下跳過)。這一個是緩慢的,它傷害:)

我怎樣才能以更優雅的方式做到這一點?是否有其他選項來阻止任務?

回答

1

轉換接收和發送環插入一個循環由發送按鈕觸發:

while(!cancel) { 
    await sendRequest(); 
    await receiveResponse(); 
} 

不知道你甚至需要循環再因爲我不知道您的具體要求。它可能看起來像這樣:

await sendRequest(); 
await receiveResponse(); 

這將執行一個請求 - 響應週期。


閱讀下面的評論我想添加以下內容:你說得對,現在你需要兩個循環。我想首先創建一個類來表示一個請求是在飛行中解決這個:

class Request { int ID; object Response; TaskCompletionCource CompletedTask; } 

當發送的東西,你創建這個類的一個實例,並將其添加到Dictionary<int, Request>ID是服務器可以用來響應您的請求的共享標識符(我瞭解多個請求可能未完成)。

收到回覆時,保存結果並將CompletedTask標記爲已完成。酸發法應該看起來像:

var request = CreateRequest(); 
await sendRequest(request); 
await request.CompletedTask.Task; 
return request.Response; 

TaskCompletionSource將作爲一個事件。類Request封裝了所有相關數據。

+0

問題是我需要接收循環,因爲這個應用程序將使用雙向JSON-RPC,因此服務器可以隨時在應用程序上執行遠程方法。 應用程序應始終查找傳入消息。 – Axadiw

+0

我已經在此添加了我的想法。 – usr

+0

好的,感謝這個,但正如我之前提到的,我需要處理超時。 有沒有辦法處理它比檢查單獨的線程更優雅,哪個TaskCompletionSource-s太舊,並手動完成它們? – Axadiw

0

好吧,

隨着USR的幫助下,我已經成功地寫代碼,阻止當前任務,與超時:

TaskTimeout擴展:

internal struct VoidTypeStruct{} 

public static class TaskTimeoutExtension 
    { 
     public static async Task TimeoutAfter(this Task task, int millisecondsTimeout) 
     { 
      if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout))) 
       await task; 
      else 
       throw new TimeoutException(); 
     } 
    } 

阻塞任務:

var tcs = new TaskCompletionSource<VoidTypeStruct>(); 

try 
{ 
    await Task.Run(async() => { await tcs.Task; }).TimeoutAfter(RequestTimeout); 
} 
catch (TimeoutException) 
{       
} 

// store tcs variable somewhere 

解鎖任務:

tcs.SetResult(new VoidTypeStruct()); 

這工作得很快(至少比以前的解決方案好得多)。

最後問題:真的,有沒有其他方法來阻止任務?關鍵部分怎麼樣,沒有任何互動任務?