8

我有一个库,它是许多网络连接的复杂仲裁者。它的主要对象的每个方法都接受一个委托,当网络响应给定请求时调用该委托。

我想翻译我的库以使用新的 .NET 4.5“async/await”模式;这将要求我返回一个“任务”对象,该对象将向用户发出调用的异步部分已完成的信号。创建这个对象需要一个函数来代表任务——据我了解,它本质上是一个轻量级线程。

这并不真正适合我的库的设计 - 我希望任务的行为更像一个事件,并直接向用户发出他们的请求已完成的信号,而不是代表一个函数。这可能吗?我应该避免以这种方式滥用“异步/等待”模式吗?

不知道我说得好不好,希望你能明白我的意思。感谢您的任何帮助。

4

2 回答 2

15

据我了解,它本质上是一个轻量级线程。

不,这不是真的。在某些情况下,我可能Task是真的,但这只是. 你可以通过传递一个委托来启动一个线程,并让它执行它(通常是异步的,可能是同步的,默认情况下使用线程池)。

使用线程的另一种方法是使用TaskCompletionSource. 当您这样做时,任务(可能)不会创建任何线程、使用线程池或任何类似的东西。此模型的一个常见用法是将基于事件的 API 转换为基于任务的 API:

让我们假设,仅仅因为它是一个常见的例子,我们希望 aTask将在From我们拥有的 a 关闭时完成。已经有一个FormClosed事件在该事件发生时触发:

public static Task WhenClosed(this Form form)
{
    var tcs = new TaskCompletionSource<object>();

    form.FormClosing += (_, args) =>
    {
        tcs.SetResult(null);
    };

    return tcs.Task;
}

我们创建一个TaskCompletionSource,向相关事件添加一个处理程序,在该处理程序中,我们发出任务完成的信号,并TaskCompletionSource为我们提供Task返回给调用者的 a 。这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();
}

这段代码现在可以正常工作了;创建一个新表单,隐藏自己,显示弹出窗口,等到弹出窗口关闭,然后再次显示自己。但是,由于它不是阻塞等待,因此 UI 线程不会被阻塞。我们也不需要为事件而烦恼;添加处理程序,处理多个上下文;移动我们的逻辑,或其中任何一个。(所有这些都发生了,只是对我们隐藏了。)

于 2012-11-21T15:11:39.813 回答
5

我想翻译我的库以使用新的 .NET 4.5“async/await”模式;这将要求我返回一个“任务”对象,该对象将向用户发出调用的异步部分已完成的信号。

好吧,不是真的 - 你可以返回任何实现等待模式的东西 - 但 aTask是最简单的方法。

这并不真正适合我的库的设计 - 我希望任务的行为更像一个事件,并直接向用户发出他们的请求已完成的信号,而不是代表一个函数。

您可以调用Task.ContinueWith以充当“处理程序”以在任务完成时执行。事实上,这就是TaskAwaiter引擎盖下的作用。

老实说,您的问题并不是很清楚,但是如果您真的想创建一个Task,然后可以随时强制完成,我怀疑您只是想要TaskCompletionSource<TResult>-您调用SetResult,SetCanceledSetException方法来指示适当的完成类型。(或者您可以调用TrySet...版本。)使用该Task属性将任务返回到任何需要它的地方。

于 2012-11-21T15:04:58.527 回答