gpt4 book ai didi

c# - 如何获得 Task.WhenAny 对 Task 和 CancellationToken 的影响?

转载 作者:太空狗 更新时间:2023-10-30 00:37:09 29 4
gpt4 key购买 nike

我有一个交互式任务,在“最坏”的情况下根本不会执行,因此它由 TaskCompletionSource 表示。

我想等待这个任务完成,或者我收到的 token 被取消——以先发生者为准。 Task.WhenAny 是完成此类工作的完美工具,唯一的问题是它只接受任务,而我有一个 Task 和一个 CancellationToken

如何等待(异步,如 Task.WhenAny)第一个事件触发——完成的任务,或取消的 token ?

async Task MyCodeAsync(CancellationToken token)
{
var tcs = new TaskCompletionSource<UserData>(); // represents interactive part

await Task.WhenAny(tcs.Task, token); // imaginary call

UserData data = tcs.Task.Result; // user interacted, let's continue
...
}

我不创建/管理 token ,因此我无法更改它。我必须处理它。

更新:对于这种特殊情况,可以在 token 上使用Register 方法来取消TaskCompletionSource。有关更通用的方法,请参阅 Matthew Watson 的回答。

最佳答案

您可以创建一个额外的任务,当取消 token 的等待句柄发出信号时返回:

var factory = new CancellationTokenSource();
var token = factory.Token;

await Task.WhenAny(
Task.Run(() => token.WaitHandle.WaitOne()),
myTask());

(但是,请注意,这虽然简单,但确实会占用一个额外的线程,这显然不理想。稍后请参阅不使用额外线程的替代解决方案。)

如果你想检查哪个任务完成了,你必须在调用 WhenAny() 之前保留任务的副本,这样你就可以将它们与返回值进行比较,例如:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static async Task Main()
{
var factory = new CancellationTokenSource(1000); // Change to 3000 for different result.
var token = factory.Token;
var task = myTask();

var result = await Task.WhenAny(
Task.Run(() => token.WaitHandle.WaitOne()),
task);

if (result == task)
Console.WriteLine("myTask() completed");
else
Console.WriteLine("cancel token was signalled");
}

static async Task myTask()
{
await Task.Delay(2000);
}
}
}

如果您不想浪费整个线程等待取消 token 发出信号,您可以使用 CancellationToken.Register() 注册一个回调,您可以使用该回调设置一个 TaskCompletionSource:

( Lifted from here )

public static Task WhenCanceled(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).SetResult(true), tcs);
return tcs.Task;
}

然后您可以按如下方式使用它:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static async Task Main()
{
var factory = new CancellationTokenSource(1000);
var token = factory.Token;
var task = myTask();

var result = await Task.WhenAny(
WhenCanceled(token),
task);

if (result == task)
Console.WriteLine("myTask() completed");
else
Console.WriteLine("cancel token was signalled");
}

public static Task WhenCanceled(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).SetResult(true), tcs);
return tcs.Task;
}

static async Task myTask()
{
await Task.Delay(2000);
}
}
}

对于一般情况,这是一种更可取的方法。

关于c# - 如何获得 Task.WhenAny 对 Task 和 CancellationToken 的影响?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57868078/

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