如何取消等待的自定义 [英] How to cancel custom awaitable

查看:99
本文介绍了如何取消等待的自定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读 Stephen Toub的博客有关等待自定义用于SocketAsyncEventArgs.一切正常.但是我需要的是可以取消的,博客不涉及这个主题.同样不幸的是,Stephen Cleary在他的书中没有介绍如何取消不支持取消的异步方法. 我尝试自己实现它,但使用TaskCompletionSource和Task.WhenAny失败,因为在等待状态下,我实际上并没有在等待任务. 这就是我想要的:能够以TAP格式使用Socket的ConnectAsync并能够取消它,同时仍然具有SocketAsyncEventArgs的可重用性.

I've read Stephen Toub's blog about making a custom awaitable for SocketAsyncEventArgs. This works all fine. But what I need is a cancellable awaitable and the blog doesn't cover this topic. Also Stephen Cleary unfortunately doesn't cover in his book how to cancel async methods that don't support cancellation. I tried to implement it myself, but I fail with the TaskCompletionSource and Task.WhenAny because with the awaitable I'm not actually awaiting a task. This is what I would like to have: being able to use Socket's ConnectAsync with TAP format and being able to cancel it, while still having the reusability of SocketAsyncEventArgs.

public async Task ConnectAsync(SocketAsyncEventArgs args, CancellationToken ct) {}

这是我到目前为止从Stephen Toub的博客中获得的内容(以及SocketAwaitable实现):

And this is what I have from Stephen Toub's blog so far (and the SocketAwaitable implementation):

public static SocketAwaitable ConnectAsync(this Socket socket,
    SocketAwaitable awaitable)
{
    awaitable.Reset();
    if (!socket.ConnectAsync(awaitable.m_eventArgs))
        awaitable.m_wasCompleted = true;
    return awaitable;
}

我只是不知道如何将其转换为TAP格式并使其可取消. 感谢您的帮助.

I just can't figure out how to get this into the TAP format and making it cancellable. Any help is appreciated.

修改1: 这是我通常如何取消的示例:

Edit 1: This is the example how I would do cancellation normally:

private static async Task<bool> ConnectAsync(SocketAsyncEventArgs args, CancellationToken cancellationToken)
{
    var taskCompletionSource = new TaskCompletionSource<bool>();

    cancellationToken.Register(() =>
    {TaskCompletionSource.Task
        taskCompletionSource.TrySetCanceled();
    });

    // This extension method of Socket not implemented yet
    var task = _socket.ConnectAsync(args);

    var completedTask = await Task.WhenAny(task, taskCompletionSource.Task);

    return await completedTask;
}

推荐答案

可等待SocketAsyncEventArgs的自定义.

custom awaitable for SocketAsyncEventArgs.

这是可行的,但是SocketAsyncEventArgs专用于对性能非常敏感的方案.绝大多数(我的意思是> 99.9%)项目不需要它,而可以使用TAP. TAP很好,因为它可以与其他技术(如取消)良好地互操作.

This is doable, but SocketAsyncEventArgs is specifically for extremely performance-sensitive scenarios. The vast majority (and I mean >99.9%) of projects do not need it and can use TAP instead. TAP is nice because it interoperates well with other techniques... like cancellation.

我需要的是可以取消的,博客没有涉及这个主题.同样不幸的是,斯蒂芬·克拉里(Stephen Cleary)在他的书中也没有介绍如何取消不支持取消的异步方法.

what I need is a cancellable awaitable and the blog doesn't cover this topic. Also Stephen Cleary unfortunately doesn't cover in his book how to cancel async methods that don't support cancellation.

因此,这里有几件事.从如何取消不支持取消的异步方法"开始:简短的答案是您不能这样做.这是因为取消是合作的;因此,如果一方不能合作,那是不可能的.更长的答案是有可能,但通常带来的麻烦多于其应有的价值.

So, there's a couple of things here. Starting with "how to cancel async methods that don't support cancellation": the short answer is that you can't. This is because cancellation is cooperative; so if one side can't cooperate, it's just not possible. The longer answer is that it's possible but usually more trouble than it's worth.

在取消不可取消的方法"的一般情况下,可以在单独的线程上同步运行该方法,然后中止该线程;这是最有效,最危险的方法,因为中止的线程很容易破坏应用程序状态.为了避免出现严重的损坏问题,最可靠的方法是在单独的进程中运行该方法,该进程可以完全终止.但是,这通常是过大的,而且麻烦多于其应有的价值.

In the general case of "cancel an uncancelable method", you can run the method synchronously on a separate thread and then abort that thread; this is the most efficient and most dangerous approach, since aborted threads can easily corrupt application state. To avoid this serious corruption problem, the most reliable approach is to run the method in a separate process, which can be terminated cleanly. However, that's usually overkill and way more trouble than it's worth.

足够的一般答案;特别是对于套接字,您无法取消单个操作,因为这会使套接字处于未知状态.以您问题中的Connect示例为例:连接与取消之间存在竞争条件.您的代码在请求取消后可能会以连接的套接字结尾,并且您不希望该资源停留在周围.因此,取消套接字连接的正常方法是关闭套接字.

Enough of the general answer; for sockets specifically, you cannot cancel a single operation, because this leaves the socket in an unknown state. Take the Connect example in your question: there's a race condition between the connection and the cancel. Your code might end up with a connected socket after it requests cancellation, and you don't want that resource sticking around. So the normal and accepted way to cancel a socket connection is to close the socket.

任何其他套接字操作也是如此:如果您需要中止读取或写入,则当您的代码发出取消指令时,它无法知道已完成多少读取/写入操作-这将使套接字退出关于您所使用的协议处于未知状态. 取消"任何套接字操作的正确方法是关闭套接字.

The same goes for any other socket operation: if you need to abort a read or write, then when your code issues a cancel, it can't know how much of the read/write completed - and that would leave the socket in an unknown state with regard to the protocol you're using. The proper way to "cancel" any socket operation is to close the socket.

请注意,此取消"的范围与我们通常看到的取消类型不同. CancellationToken和朋友代表取消单个操作;关闭套接字会取消该套接字的所有操作.因此,套接字API不使用CancellationToken参数.

Note that this "cancel" is at a different scope than the kind of cancellation we normally see. CancellationToken and friends represent the cancellation of a single operation; closing a socket cancels all operations for that socket. For this reason, the socket APIs do not take CancellationToken parameters.

不幸的是,斯蒂芬·克莱里(Stephen Cleary)在他的书中没有介绍如何取消不支持取消的异步方法.我尝试自己实施

Stephen Cleary unfortunately doesn't cover in his book how to cancel async methods that don't support cancellation. I tried to implement it myself

如何取消不可取消的方法"问题几乎从未解决过,使用此代码块"已被正确解决.具体来说,您使用的方法取消了 await ,而不是 operation .请求取消后,您的应用程序仍在尝试连接到服务器,并且可能最终成功或失败(两种结果都将被忽略).

The question "how do I cancel an uncancelable method" is almost never properly solved with "use this code block". Specifically, the approach you used cancels the await, not the operation. After a cancel is requested, your application is still trying to connect to the server, and may eventually succeed or fail (both results would be ignored).

在我的AsyncEx库中,我确实有一个夫妇 > WaitAsync 重载,使您可以执行以下操作:

In my AsyncEx library, I do have a couple of WaitAsync overloads, which allow you to do something like this:

private static async Task MyMethodAsync(CancellationToken cancellationToken)
{
  var connectTask = _socket.ConnectAsync(_host);
  await connectTask.WaitAsync(cancellationToken);
  ...
}

我更喜欢这种API,因为很明显,取消了异步等待,而不是不是连接操作.

I prefer this kind of API because it's clear that it's the asynchronous wait that is cancelled, not the connection operation.

我在使用TaskCompletionSource和Task.WhenAny时失败,因为在等待时我实际上没有在等待任务.

I fail with the TaskCompletionSource and Task.WhenAny because with the awaitable I'm not actually awaiting a task.

好吧,要执行类似这样的更复杂的操作,您需要将等待的值转换为Task.也许有一个更有效的选择,但这确实是杂草丛生.而且,即使您弄清了所有细节,您仍然会以假取消"结束:看起来的API就像它取消了操作,但实际上并没有-它只是取消了等待

Well, to do something more complex like this, you'll need to convert that awaitable to a Task. There may be a more efficient option, but that's really getting into the weeds. And even if you figure out all the particulars, you'll still end up with "fake cancelling": an API that looks like it cancels the operation but it really doesn't - it just cancels the await.

这就是我想要的:能够以TAP格式使用Socket的ConnectAsync并能够取消它,同时仍然具有SocketAsyncEventArgs的可重用性.

This is what I would like to have: being able to use Socket's ConnectAsync with TAP format and being able to cancel it, while still having the reusability of SocketAsyncEventArgs.

TL; DR:您应该关闭套接字,而不是取消连接操作.就快速回收资源而言,这是最干净的方法.

TL;DR: You should close the socket instead of cancelling a connection operation. That's the cleanest approach in terms of reclaiming resources quickly.

这篇关于如何取消等待的自定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆