gpt4 book ai didi

c# - 如何调整 DatagramSocket.MessageReceived 以与异步/等待一起使用?

转载 作者:太空狗 更新时间:2023-10-29 23:40:00 25 4
gpt4 key购买 nike

我想用 TPL 包装以下数据报套接字操作以清理 API,以便它与 async 一起很好地工作。和 await ,很像 StreamSocket类。

public static async Task<bool> TestAsync(HostName hostName, string serviceName, byte[] data)
{
var tcs = new TaskCompletionSource<bool>();
var socket = new DatagramSocket();
socket.MessageReceived += (sender, e) =>
{
var status = false; // Status value somehow derived from e etc.
tcs.SetResult(status);
};
await socket.ConnectAsync(hostName, serviceName);
var stream = await socket.GetOutputStreamAsync();
var writer = new DataWriter(stream);
writer.WriteBytes(data);
await writer.StoreAsync();
return tcs.Task;
}

关键是 MessageReceived变成 DatagramSocket 的事件类变成了事件异步模式和新的 async 的奇怪混杂。图案。无论如何,TaskCompletionSource<T>允许我调整处理程序以符合后者,所以这并不太可怕。

除非端点从不返回任何数据,否则这似乎工作得很好。与 MessageReceived 关联的任务处理程序永远不会完成,因此任务从 TestAsync 返回永远不会完成。

完美包装此操作以合并超时和取消的正确方法是什么?我想扩展此功能以获取 CancellationToken后者的论点,但我该怎么办呢?我唯一想到的就是使用 Task.Delay 创建一个额外的“监控”任务我向其传递超时值和取消 token 以支持以下两种行为:

public static async Task<bool> CancellableTimeoutableTestAsync(HostName hostName, string serviceName, byte[] data, CancellationToken userToken, int timeout)
{
var tcs = new TaskCompletionSource<bool>();
var socket = new DatagramSocket();
socket.MessageReceived += (sender, e) =>
{
var status = false; // Status value somehow derived from e etc.
tcs.SetResult(status);
};
await socket.ConnectAsync(hostName, serviceName);
var stream = await socket.GetOutputStreamAsync();
var writer = new DataWriter(stream);
writer.WriteBytes(data);
await writer.StoreAsync();

var delayTask = Task.Delay(timeout, userToken);
var t1 = delayTask.ContinueWith(t => { /* Do something to tcs to indicate timeout */ }, TaskContinuationOptions.OnlyOnRanToCompletion);
var t2 = delayTask.ContinueWith(t => { tcs.SetCanceled(); }, TaskContinuationOptions.OnlyOnCanceled);

return tcs.Task;
}

然而,这有各种各样的问题,包括延迟任务和 MessageReceived 之间的潜在竞争条件。处理程序。我从来没有能够让这种方法可靠地工作,而且它看起来非常复杂,而且线程池的使用效率低下。它很繁琐、容易出错并且伤了我的头。

旁注:我是唯一一个对 DatagramSocket 感到困惑的人吗? API一般?它不仅看起来是IAsyncAction 的丑陋集团WinRT 模型和带有一些棘手的 EAP 的 TPL,我对旨在表示基本无连接协议(protocol)的 API 不太满意,例如包含名为 ConnectAsync 的方法的 UDP。在他们中。这对我来说似乎是矛盾的。

最佳答案

首先,我认为 DatagramSocket 的接口(interface)之所以有意义,正是因为 UDP 的性质。如果您有数据报流,那么事件是表示它的合适方式。 WinRT IAsyncAction(或 .Net Task)只能表示拉模型,您可以在其中明确请求每条数据(例如,可能有一个方法 ReadNextDatagramAsync())。这对 TCP 来说是有意义的,因为它有流量控制,所以如果你读取数据很慢,发送方也会很慢地发送它们。但对于 UDP,推送模型(由 WinRT 和 .Net 中的事件表示)更有意义。

我同意 Connect 这个名字没有 100% 的意义,但我认为 moSTLy 确实有意义,尤其是让它与 StreamSocket。而你确实需要一个这样的方法,以便系统可以解析域名并为你的套接字分配一个端口。

对于您的方法,我同意@usr 的观点,即您应该创建一个单独的方法来接收数据报。而且,如果您想将一个异步模型转换为另一个异步模型,同时添加原始模型本身不支持的功能,那将会很麻烦,我认为您对此无能为力。

它也不会低效,如果你正确地实现它:你应该确保在 Task 完成后,取消订阅 MessageReceived 事件,计时器与 Delay() 关联的对象被释放(您可以通过取消传递给 Delay() 的 token 来实现)以及使用传入的 注册的委托(delegate)CancellationToken 未注册(我认为您应该直接使用 Register() 而不是 (ab) 为此使用 Delay())。

关于竞争条件,你当然要考虑它们。但是这里有一个相对简单的方法来处理这个问题:使用 TaskCompletionSourceTry 方法(例如 TrySetResult() )。

关于c# - 如何调整 DatagramSocket.MessageReceived 以与异步/等待一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14766394/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com