2

好的,首先,我不需要实现任何东西。我只需要知道答案,因为更有经验的人告诉我,异步执行不一定要涉及新线程,因为线程是有些沉重的结构,这让我很困惑,我不能同意。

现在假设,我有两种方法 -Execute()ExecuteAsync(). Execute()在主线程上运行。我想ExecuteAsync()从内部调用Execute(),我不在乎它何时完成执行,但是当它执行时,可能(或可能不是)我想使用它的返回值。这是异步执行的典型示例,对吧?

我知道我可以使用BackgroundWorkeror IAsyncResult( Delegate.BeginInvoke()) 来做到这一点,但是 AFAIK 在引擎盖下会产生一个辅助 CLR 线程/线程池线程。

那么是否有可能在ExecuteAsync()没有第二个线程的帮助的情况下异步执行该方法?

编辑:我认为这个编辑将进一步阐明这种情况。调用ExecuteAsync()不是唯一(或最后)Execute()要执行的任务。Execute()应该继续它自己的任务而不关心ExecuteAsync()方法的执行。

4

3 回答 3

3

这是一个使用异步且从不使用多个线程的程序示例:

public class Foo
{
    private int _value;
    private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    public int Value
    {
        get
        {
            return _value;
        }
        set
        {
            _value = value;
            var oldTCS = tcs;
            tcs = new TaskCompletionSource<bool>();
            oldTCS.SetResult(true);
        }
    }


    public Task ValueChanged()
    {
        return tcs.Task;
    }
}

private static void Main(string[] args)
{
    Foo foo = new Foo();
    foo.ValueChanged()
        .ContinueWith(t =>
        {
            Console.WriteLine(foo.Value);
        }, TaskContinuationOptions.ExecuteSynchronously);

    foo.Value = 5;
}

Task返回的 from将ValueChanged在下次Value更改时完成。该类的用户Foo可以获取该返回的任务,并根据尚未发生的操作连接延续以在该任务上运行。然后,在未来的某个时间点, 的值发生了foo变化,延续将运行。请注意, foo 对象可以传递给其他一些完全未知的函数,Main最终设置该值(以说明为什么您可能想要做这样的事情)。

不需要新线程来创建Task,也不需要执行延续。

这是另一个更实用的示例:

我们将从这个简单的(扩展)方法开始,它接受一个表单并返回一个Task指示该表单下一次关闭的时间:

public static class FormExtensions
{
    public static Task WhenClosed(this Form form)
    {
        var tcs = new TaskCompletionSource<bool>();
        form.FormClosed += (sender, args) => tcs.SetResult(true);
        return tcs.Task;
    }
}

现在我们可以用我们的一种形式来实现它:

private async void button1_Click(object sender, EventArgs args)
{
    Form2 otherForm = new Form2();
    otherForm.Show();

    await otherForm.WhenClosed();

    //take some data from that form and display it on this form:
    textBox1.Text = otherForm.Name;
}

创建和显示另一种形式从不涉及创建新线程。此表单和新表单都完全使用一个 UI 线程来创建和修改。

Task返回 from的创建WhenClosed根本不需要创建新线程。

Task等待时,不会创建新线程。当前方法结束,UI 线程返回处理消息。在某些时候,同一个 UI 线程会做一些导致第二个表单关闭的事情。这将导致任务继续运行,从而将我们返回到我们设置文本框文本的按钮单击处理程序。

所有这些都完全由 UI 线程完成,没有创建其他线程。然而我们只是“等待”(实际上没有等待)一个长时间运行的操作完成(用户将一些信息放入第二个表单然后关闭它)而不阻塞 UI 线程,从而保持主表单响应。

于 2013-01-23T19:03:17.457 回答
1

那么是否有可能在没有第二个线程的帮助的情况下异步执行 ExecuteAsync() 方法?

某些方法可以在不使用新线程的情况下异步运行。例如,这可以通过带有信号的异步 I/O来完成。尽可能在 .NET 4.5 异步 IO 中添加框架的大多数新异步方法,而不是线程。

这就是为什么不要假设异步 == 新线程是个好主意。虽然可以使用线程来实现异步,但并不总是以这种方式实现。最好将异步操作视为(希望)不会阻塞并在将来某个时间点完成的操作。

于 2013-01-23T18:51:21.593 回答
1

协程是使用单个物理线程实现多个逻辑线程的常用方法。较旧的操作系统使用此概念和其他相关概念来实现协作多任务

在这种情况下,您可能还对延续传递风格感兴趣,Eric Lippert有一个关于这个主题的很好的博客系列 -第 1部分、第 2部分、第 3部分、第 4部分、第 5 部分

于 2013-01-23T18:54:35.523 回答