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&lt;int, Request&gt;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:阻止任务的主要内容,如果未能解决你的问题,请参考以下文章

Celery:当排队太多时阻止添加更多任务

29任务二十九——阻止点击之后的默认事件

FreeRTOS,Atmel Studio - 任务在收到二进制信号量之前没有被阻止

Java:在特定队列大小后阻止提交的 ExecutorService [重复]

Flutter - 如何在不阻止UI的情况下计算包含未来的繁重任务?

Angular 2 - 如何在等待 http 请求完成时阻止 GUI 渲染