WinRT的:封锁任务(WinRT: blocking Task)

2019-10-17 10:32发布

我实施我的应用程序,即使用异步JSON-RPC协议的网络层。

为了与服务器通信,我想提出,将发送一个适当的请求,等待服务器响应发送,并返回它的方法。 在使用异步的一切/等待关键字。

下面是简单的示例代码:

串响应;

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)以处理这个问题,但它冻结整个主题,因为我使用异步/只等待着,它冻结整个应用程序(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();      
}

用该溶液(除了它看起来像一个黑客)的问题是性能 - 每一个请求有抛出EXCETION,需要处理(在这种情况下跳过)。 这是一个缓慢的,它伤害:)

我怎样才能做到这一点更优雅的方式? 是否有任何其他选项来阻止一个任务?

Answer 1:

转换接收和发送环插入一个循环由发送按钮触发:

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

不知道你甚至需要循环再因为我不知道您的具体要求。 它看起来是这样的:

await sendRequest();
await receiveResponse();

这将执行单个请求 - 响应周期。


读您的评论如下我想补充如下:你说的没错,现在你需要两个循环。 我想首先创建一个类来表示一个请求是在飞行中解决这个:

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

当发送的东西,你创建这个类的一个实例,并将其添加到Dictionary<int, Request> 。 该ID是一个共享标识符,所述服务器可用于响应您的请求(I理解多个请求可以是优秀)。

当接收到响应您保存结果并标记CompletedTask为已完成。 酸奶发送方法应该是这样的:

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

TaskCompletionSource将作为一个事件。 该Request类封装了所有相关数据。



Answer 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: blocking Task