我很确定这里有一个重复项,但找不到。
很简单,这样做是否更好:
await MyMethod();
使用:
public async Task MyMethod()
{
Thread.Sleep(10000); // Simulates long running operation
}
还是与Task.Run有关?
public async Task MyMethod()
{
await Task.Run(() => Thread.Sleep(10000)); // Simulates long running operation
}
实际上长时间运行的操作是不支持异步的阻塞调用。
第一个示例显然使Visual Studio抱怨它将同步运行。使该方法异步的原因是,我希望人们能够轻松await
,以免他们占用线程。
答案 0 :(得分:5)
在第一个示例中使用await
是没有用的,因为Task
仅在方法完成后返回,此时Task
也将完成。
我也不会使用第二个示例;不要假设调用者希望该方法在线程池上运行;让他们自己决定:
public void MyMethod()
{
Thread.Sleep(10000); // Simulates long running operation
}
来电者:
await Task.Run(MyMethod);
我认为这里要说明的关键是,如果方法不是真正的异步方法,则不应声称是这样。
在该行的某处,一个线程被阻塞,无论该线程是当前(UI)线程,还是线程池上的另一个线程。
Task.Run
由于线程之间的切换而带来开销,具体取决于使用方。
在ASP.NET环境中,最好同步运行该方法。 HTTP响应只能在操作完成后发送,所以为什么要增加开销?
在桌面环境中,保持UI线程的响应速度很关键,因此创建的开销值得。
答案 1 :(得分:3)
是否使用async
取决于此代码是否被阻止。如果它正在阻塞并且您无法使其解除阻塞,那么就不要使用async
,而只需要处理阻塞代码即可。如果您可以使其变为非阻塞状态,请使用async
。 (使用Task.Run()
而不是Thread
添加线程取决于此代码在做什么,是IO or CPU bound吗?
如果此代码被阻止,并且您无法更改,则正确的实现将是:
public void MyMethod()
{
// Long running operation
}
实际上,如果您的代码正在阻塞并且不包含任何await
调用,这正是预编译器将您的方法转换为(是否为async
)的方式。
通常,如果您使用async
,则始终使用Task
类(及其方法),因为这会在基础线程上添加一层抽象并启用async
模式。 TBH几乎一直都在使用Task
,很少有人因为TPL而需要直接与线程进行交互。在代码审查中,我基本上拒绝了现在使用Thread
的任何人,并告诉他们改为使用Task
。
模拟延迟async
方法的正确方法是使用Task.Delay()
,因此:
public async Task MyMethod()
{
await Task.Delay(10000).ConfigureAwait(false); // Simulates long running operation
}
这将在发生延迟时将线程释放回线程池。您当前的方法阻塞了主线程,因此优化程度较差。 ConfigureAwait(false)
仅添加了更多optimisation around context switching(这取决于您的上下文,因此请阅读该链接)。
这种方法是两全其美的方法:
public async Task MyMethod()
{
await Task.Run(() => Thread.Sleep(10000)); // Simulates long running operation
}
这现在释放了主线程(我想很好),但是它所做的就是创建一个新线程(包含所有上下文切换的开销),然后将其阻塞。因此,您仍在使用一个线程,该线程仍处于阻塞状态,但现在您刚刚将大量额外的上下文切换添加到混合中,以减慢所有操作。所以这效率很低。
答案 2 :(得分:0)
使方法异步的原因是因为我希望人们成为 能够轻松等待,以免他们占用线程。
如果您绝对希望人们能够等待它,并且您拥有完全同步的代码,则无法对其进行更改以使其返回可等待的代码,并且肯定会阻塞调用线程,并且您不希望这样做,那么您唯一的选择是使用Task.Run()
(或其衍生版本)。
请注意,这被许多人视为反模式。正确的方法是更改您的要求,以使其不必等待。
public async Task MyMethod()
{
await Task.Run(() => LongRunningBlockingSyncMethod());
}