我有一个库,它是许多网络连接的复杂仲裁器.它的主要对象的每个方法都有一个委托,当网络响应给定的请求时会调用该委托.
我想翻译我的库以使用新的.NET 4.5"async/await"模式; 这将要求我返回一个"任务"对象,该对象将向用户发出呼叫的异步部分已完成的信号.创建这个对象需要一个代表任务的函数 - 据我所知,它本质上是一个轻量级的线程.
这并不适合我的库的设计 - 我希望任务的行为更像是一个事件,并直接向用户发出他们的请求已经完成的信号,而不是代表一个函数.这可能吗?我应该避免以这种方式滥用"异步/等待"模式吗?
我不知道我的措辞是否很好,我希望你理解我的意思.感谢您的任何帮助.
Ser*_*rvy 10
据我所知,它本质上是一个轻量级的线程.
不,那不是真的.在某些情况下,我可以是真的,但这只是一种用法Task.你可以通过传递一个委托并让它执行它来启动一个线程(通常是异步的,可能是同步的,并且默认情况下使用线程池).
使用线程的另一种方法是使用a TaskCompletionSource.当您这样做时,任务(可能)不会创建任何线程,使用线程池或沿着这些线的任何东西.此模型的一个常见用法是将基于事件的API转换为基于任务的API:
让我们假设,只是因为它是一个常见的例子,我们希望有一个Task当将要完成From,我们已经被关闭.FormClosed事件发生时已经发生了一个事件:
public static Task WhenClosed(this Form form)
{
var tcs = new TaskCompletionSource<object>();
form.FormClosing += (_, args) =>
{
tcs.SetResult(null);
};
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
我们创建一个TaskCompletionSource,为有问题的事件添加一个处理程序,在那个处理程序中我们发出任务完成的信号,并TaskCompletionSource为我们提供一个Task返回给调用者.这Task不会导致创建任何新线程,它不会使用线程池或类似的东西.
您可以使用此构造使用基于任务/事件的模型,该构造看起来非常异步,但只使用单个线程来完成所有工作(UI线程).
一般来说,任何时候你想要一个Task代表一个函数执行以外的东西,你会想要考虑使用TaskCompletionSource.它通常是来解决这个问题的适当的概念的方式,比可能使用的现有TPL方法之一,如其他WhenAll,WhenAny等
我应该避免以这种方式滥用"异步/等待"模式吗?
不,因为它不是滥用.它非常适合使用Task构造,以及async/await.例如,考虑一下您可以使用上面的辅助方法编写的代码:
private async void button1_Click(object sender, EventArgs e)
{
Form2 popup = new Form2();
this.Hide();
popup.Show();
await popup.WhenClosed();
this.Show();
}
Run Code Online (Sandbox Code Playgroud)
这段代码现在就像读取一样; 创建一个新表单,隐藏自己,显示弹出窗口,等待弹出窗口关闭,然后再次显示自己.但是,由于它不是阻塞等待,因此UI线程不会被阻止.我们也不需要打扰事件; 添加处理程序,处理多个上下文; 移动我们的逻辑或任何一个.(所有这一切都发生了,它只是隐藏在我们身上.)
我想翻译我的库以使用新的 .NET 4.5“async/await”模式;这将要求我返回一个“任务”对象,该对象将向用户发出调用的异步部分已完成的信号。
嗯,不是真的 - 你可以返回任何实现可等待模式的东西 - 但 aTask是最简单的方法。
这并不真正适合我的库的设计 - 我希望任务表现得更像一个事件,并直接向用户发出他们的请求已完成的信号,而不是代表一个函数。
您可以调用Task.ContinueWith充当“处理程序”以在任务完成时执行。事实上,这就是TaskAwaiter引擎盖下的作用。
说实话,你的问题不是很清楚,但如果你真的想创建一个Task你可以随时强制完成的,我怀疑你只是想要TaskCompletionSource<TResult>- 你调用SetResult,SetCanceled或SetException方法来指示适当的完成类型。(或者您可以调用TrySet...版本。)使用该Task属性将任务返回到任何需要它的地方。