WinRT:阻止任务
Posted
技术标签:
【中文标题】WinRT:阻止任务【英文标题】:WinRT: blocking Task 【发布时间】:2012-11-26 09:15:02 【问题描述】:我正在实现我的应用程序的网络层,即使用异步 JSON-RPC 协议。
为了与服务器通信,我想做一个方法来发送正确的请求,等到服务器发送响应,然后返回它。一切都使用 async/await 关键字。
这里是简化的示例代码:
字符串响应;
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) 来处理这个问题,但它会冻结整个线程,并且因为我只使用 async/await,它会冻结整个应用程序(对我来说更准确的是 UI 线程)。
对我来说看起来很老套的解决方案是我可以将 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();
该解决方案的问题(除了看起来像黑客之外)是性能 - 每个请求都会引发异常,需要处理(在这种情况下跳过)。这个很慢,很痛:)
我怎样才能以更优雅的方式做到这一点?是否有任何其他选项可以阻止任务?
【问题讨论】:
【参考方案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
类封装了所有相关数据。
【讨论】:
问题是我需要recieve循环,因为这个应用程序将使用双向JSON-RPC,所以服务器可以随时在应用程序上执行远程方法。应用应始终查找传入消息。 我已经添加了我的想法。 好的,感谢这个,但正如我之前提到的,我需要处理超时。有什么办法比检查单独的线程更优雅,哪个 TaskCompletionSource-s 太旧了,然后手动完成它们? 您可以像这样使您的操作超时:blogs.msdn.com/b/pfxteam/archive/2011/11/10/10235834.aspx 这在幕后完成了 TCS。 在我下面的帖子中(有完整的解决方案)我已经提出了一个问题,但它可以被忽略,所以我会在这里再问一遍:“真的,没有其他方法可以阻塞任务?临界区呢,任务没有互斥体吗?”【参考方案2】:好的,
在 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());
这工作得非常快(至少比以前的解决方案好得多)。
最后一个问题:真的,没有其他方法可以阻止任务吗?关键部分呢,任务没有互斥体吗?
【讨论】:
以上是关于WinRT:阻止任务的主要内容,如果未能解决你的问题,请参考以下文章
FreeRTOS,Atmel Studio - 任务在收到二进制信号量之前没有被阻止
Java:在特定队列大小后阻止提交的 ExecutorService [重复]