2012-11-26 68 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()); 

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

最后问题:真的,有没有其他方法来阻止任务?关键部分怎么样,没有任何互动任务?