1

所以我最近一直在阅读大量关于网络异步 CTP 的内容,并且不断出现的一件事是这样的句子:“异步不是关于启动新线程,而是关于多路复用工作”和“没有多线程的异步是相同的想法[与协作多任务处理]。您执行一项任务一段时间,当它产生控制权时,您在该线程上执行另一项任务一段时间“。

我试图了解这样的言论是否纯粹(嗯,主要是)深奥和学术,或者是否有一些我忽略的语言结构允许我通过“等待”开始的任务神奇地在 UI 线程上运行。

在他的博客中,Eric Lippert 给出了这个例子来演示如何在没有多线程的情况下使用 Asyncrhony:

async void FrobAll()
{
    for(int i = 0; i < 100; ++i)
    {
        await FrobAsync(i); // somehow get a started task for doing a Frob(i) operation on this thread
    }
} 

现在,这是让我感兴趣的评论:“......获得一个开始的任务,在这个线程上执行 Frob(i) 操作”。

这怎么可能?这主要是理论上的评论吗?到目前为止,我能看到的唯一一个任务似乎不需要单独线程的情况(好吧,除非你检查代码,否则你无法确定)就像 Task.Delay() 之类的,可以在没有的情况下等待开始另一个线程。但我认为这是一种特殊情况,因为我没有为此编写代码。

对于想要从 GUI 线程中卸载一些他们自己的长时间运行的代码的普通用户来说,我们主要不是在谈论像 Task.Run 这样的事情来卸载我们的工作吗?线?如果是这样,为什么所有这些手臂都放弃不将异步与多线程混淆?

4

3 回答 3

2

请参阅我async/await的介绍。

在 CPU 工作的情况下,您必须使用类似的东西Task.Run在另一个线程中执行它。但是,有很多工作不是 CPU 工作(网络请求、文件系统请求、计时器等),而且每一个都可以在Task 使用线程的情况下包装在 a 中。

请记住,在驱动程序级别,一切都是异步的。同步 Win32 API 只是方便的包装器。

于 2012-08-02T14:28:50.240 回答
2

不是 100% 肯定,但从文章中听起来好像是它允许 Windows 消息(调整大小事件、鼠标点击等)在对FrobAsync. 大致类似于:

void FrobAll()
{
    for(int i = 0; i < 100; ++i)
    {
        FrobAsync(i); // somehow get a started task for doing a Frob(i) operation on this thread
        System.Windows.Forms.Application.DoEvents();
    }
}

或者更准确地说:

void FrobAll()
{
    SynchronizationContext.Current.Post(QueueFrob, 0);
} 

void QueueFrob(Object state) {
    var i = (int)state;
    FrobAsync(i);
    if (i == 99) return;
    SynchronizationContext.Current.Post(QueueFrob, i+1);
}

DoEvents没有每次迭代之间讨厌的调用。发生这种情况的原因是因为对 await 的调用发送了一条 Windows 消息,该消息将该FrobAsync调用排入队列,从而允许在下一次 Frobbing 开始之前执行在 Frobbing 之间发生的任何 Windows 消息。

于 2012-08-02T14:35:51.213 回答
1

async/await只是关于异步。从调用方来看,是否使用多个线程并不重要——只是它做一些异步的事情。例如,IO 完成端口是异步的,但不执行任何多线程(操作的完成发生在后台线程上;但“工作”并未在该线程上完成)。

await关键字来看,“多线程”是没有意义的。异步操作所做的是一个实现细节。

在 Eric 的示例中,该方法可以按如下方式实现:

return Task.Factory.StartNew(SomeMethod, 
                             TaskScheduler.FromCurrentSynchronizationContext());

SomeMethod这实际上意味着当它完成所有当前排队的工作时,在当前线程上排队调用。这与可能在执行之前返回的FrobAsyncin的调用者是异步的。FrobAsyncSomeMethod

现在,FrobAsync可以实现使用多线程,在这种情况下,它可以这样写:

return Task.Factory.StartNew(SomeMethod);

如果未更改默认 TaskScheduler ,则使用线程池。但是,从调用者的角度来看,什么都没有改变——你仍然await是方法。

从多线程的角度来看,这是你应该看的。使用Task.Start,Task.RunTask.Factory.StartNew.

于 2012-08-02T14:47:45.420 回答