Task ExecuteProcess(string path)
{
var p = new Process() { EnableRaisingEvents = true, StartInfo = { FileName = path } };
var tcs = new TaskCompletionSource<bool>();
p.Exited += (sender, args) => { tcs.SetResult(true); p.Dispose(); };
// запуск выгружаем на пул потоков, потому что он медленный
Task.Run(() => p.Start());
return tcs.Task;
}
另一个例子取自DispatcherThread. 我们需要等到线程启动并进入“工作”状态。这通常用于 this AutoResetEvent,但在等待时不愿意阻塞,并且使用起来要容易得多TaskCompletionSource:
static public Task<DispatcherThread> CreateAsync()
{
var waitCompletionSource = new TaskCompletionSource<DispatcherThread>();
var thread = new Thread(() =>
{
// тут могут быть любые настройки
waitCompletionSource.SetResult(new DispatcherThread());
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return waitCompletionSource.Task;
}
TaskCompletionSource- 当您无法Task使用标准工具创建“基础”时,这是非常极端的情况。让我解释一下我的意思。如果您正在创建
Task,有两种常见的方法可以做到这一点。首先,如果您的代码不等待,而是正在积极运行,例如,执行计算(受 CPU 限制),您可以使用
Task.Run或其类似物将其发送到线程池。其次,如果您使用其他异步操作,您将创建
async一个在其中执行await其他异步操作的方法。.NET 提供了许多开箱即用的异步操作,例如 .NETNetworkStream.ReadAsync或Dispatcher.InvokeAsync.但是,如果您需要自己创建一个原始异步操作,而不是用其他现成的异步操作来表达怎么办?最里面的
Task方法是如何创建的?这是您需要它的地方
TaskCompletionSource。例如,您想异步等待一个事件。为此,您需要将事件转换为
Task. 这样做是这样的:我们订阅一个事件,当它到达时,我们完成Task.一个更严格的取消订阅选项,它
TaskCompletionSource被用作内部选项Task,以便在结束后有时间取消订阅:真实代码中的另一个示例:启动并等待进程结束:
另一个例子取自
DispatcherThread. 我们需要等到线程启动并进入“工作”状态。这通常用于 thisAutoResetEvent,但在等待时不愿意阻塞,并且使用起来要容易得多TaskCompletionSource:Task我们看到,通过这种方式,基本上可以变成任何操作。其他相关阅读:TPL 和传统 .NET Framework 异步编程。
摘要:甚至
TaskCompletionSource可以让您变成Task不提供await-able接口的异步操作。有一个关于 SO 的问题。在这里阅读。最佳答案之一的翻译:
根据我的经验
TaskCompletionSource,非常适合将旧的异步模板迁移到现代模板async/await。我能想到的最有用的例子是使用
Socket. 它具有旧的 APM 和 EAP 模板,但没有awaitable Task具有TcpListener和TcpClient.我个人在课堂上有一些问题,我
NetworkStream更喜欢raw Socket. 因为我也喜欢这个模板async/await,所以我创建了一个扩展类SocketExtender,它为Socket.所有这些方法都用于
TaskCompletionSource<T>包装异步调用,如下所示:我传入
Socket方法BeginAccept,以便从编译器获得小的性能提升,而无需提高本地参数。那么这一切的美丽: