我是TPL的新手,我想知道:C#5.0新增的异步编程支持(通过新的async
和await
关键字)与创建有什么关系?线程?
具体来说,async/await
每次使用它们时都会创建一个新线程吗?如果有许多嵌套方法使用async/await
,是否为每个方法创建了一个新线程?
答案 0 :(得分:44)
简而言之否
来自Asynchronous Programming with Async and Await : Threads
async和await关键字不会导致额外的线程 创建。异步方法不需要多线程,因为异步 方法不会在自己的线程上运行。该方法在当前运行 同步上下文并仅在线程上使用时间 方法是有效的。您可以使用Task.Run将CPU绑定的工作移动到 后台线程,但后台线程对进程没有帮助 只是等待结果可用。
答案 1 :(得分:4)
根据MSDN:async keyword
异步方法同步运行,直到到达其第一个await表达式为止,这时该方法将挂起,直到完成等待的任务。同时,控制权返回到方法的调用者,如下一节中的示例所示。
下面是检查它的示例代码:
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
private void Print(string txt)
{
string dateStr = DateTime.Now.ToString("HH:mm:ss.fff");
Console.WriteLine($"{dateStr} Thread #{Thread.CurrentThread.ManagedThreadId}\t{txt}");
}
private void Run()
{
Print("Program Start");
Experiment().Wait();
Print("Program End. Press any key to quit");
Console.Read();
}
private async Task Experiment()
{
Print("Experiment code is synchronous before await");
await Task.Delay(500);
Print("Experiment code is asynchronous after first await");
}
}
在另一个线程上执行后,我们看到了Experiment()方法的代码。
但是如果我用自己的代码(方法SomethingElse)替换Task.Delay:
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
private void Print(string txt)
{
string dateStr = DateTime.Now.ToString("HH:mm:ss.fff");
Console.WriteLine($"{dateStr} Thread #{Thread.CurrentThread.ManagedThreadId}\t{txt}");
}
private void Run()
{
Print("Program Start");
Experiment().Wait();
Print("Program End. Press any key to quit");
Console.Read();
}
private async Task Experiment()
{
Print("Experiment code is synchronous before await");
await SomethingElse();
Print("Experiment code is asynchronous after first await");
}
private Task SomethingElse()
{
Print("Experiment code is asynchronous after first await");
Thread.Sleep(500);
return (Task.CompletedTask);
}
}
我注意到线程保持不变!
最后,我要说的是异步/等待代码可以使用另一个线程,但前提是该线程是由另一个代码而不是由异步/等待创建的。
在这种情况下,我认为Task.Delay
创建了线程,因此我可以断定async / await不会像@Adriaan Stander所说的那样创建新线程。
答案 2 :(得分:2)
因此,我一直在阅读线程模型,Async / Await肯定会导致使用新线程(不一定创建-池在应用程序启动时创建它们)。由调度程序确定是否需要新线程。正如我所看到的,对一个等待函数的调用可能具有内部细节,从而增加了调度程序利用另一个线程的机会。仅仅是因为更多的工作意味着调度程序分配工作的更多机会/原因。
WinRT异步操作自动在线程池上发生。通常,除了UI线程工作.. Xaml / Input / Events外,您将从线程池中调用。
在Xaml / UI线程上启动的异步操作将其结果传递回[calling] UI线程。但是从线程池线程开始的异步操作结果将在完成发生的任何地方传递,这可能与您之前使用的线程不同。其背后的原因是,为线程池编写的代码可能被编写为线程安全的,并且也是出于提高效率的考虑,Windows不必协商该线程切换。
因此,再次回答OP,不一定要创建新线程,但是您的应用程序可以并且将使用多个线程来完成异步工作。
我知道这似乎与有关异步/等待的一些文献相矛盾,但这是因为尽管异步/等待构造本身不是多线程的。等待是调度程序可以用来划分工作并在线程之间构造调用的机制之一,或者是机制之一。
这是我目前关于异步和线程的知识的极限,因此我可能并不完全正确,但是我认为重要的是要看到等待的对象和线程之间的关系。
答案 3 :(得分:1)
很抱歉参加晚会。
我是TPL的新手,我想知道:异步如何 C#5.0新增的编程支持(通过新的async和await 关键字)与线程的创建有关?
async/await
并不是为了创建线程而引入的,而是为了最佳地利用当前线程。
您的应用程序可能会读取文件,等待其他服务器的响应,甚至进行具有高内存访问权限的计算(仅执行任何IO任务)。这些任务不占用大量CPU资源(任何不会占用您线程100%的任务)。
考虑您正在处理1000个非CPU密集型任务。在这种情况下,创建1000个OS级线程的过程可能比在单个线程上进行实际工作消耗更多的CPU和内存(Windows中每个线程4mb,4MB * 1000 = 4GB )。同时,如果您依次运行所有任务,则可能必须等到IO任务完成为止。最终导致长时间完成任务,同时保持CPU空闲。
由于我们要求并行处理才能快速完成多个任务,因此所有并行处理任务都不占用CPU,但是创建线程效率很低。
一旦到达async
,编译器将在对awaited
方法的任何方法调用中中断执行(无论是否为await
),并立即执行其余代码。 ,执行将进入先前的async
内部。这将一次又一次重复,直到所有异步调用都完成并且它们的awaiters
得到满足。
如果任何异步方法在没有调用异步方法的情况下占用大量CPU资源,那么是的,您的系统将变得无响应,并且所有剩余的异步方法都将在当前任务完成之前被调用。
答案 4 :(得分:-3)
使用异步/等待并不一定会导致创建新线程。但是使用Async / Await可能会导致创建新线程,因为awaitable函数可能会在内部产生新线程。它经常这样做,实际上使“不,它不会产生线程”这样的语句几乎没有用。例如,以下代码产生新线程。
VisualProcessor.Ctor()
{
...
BuildAsync();
}
async void BuildAsync()
{
...
TextureArray dudeTextures = await TextureArray.FromFilesAsync(…);
}
public static async Task<TextureArray> FromFilesAsync(...)
{
Debug.WriteLine("TextureArray.FromFilesAsync() T1 : Thread Id = " + GetCurrentThreadId());
List<StorageFile> files = new List<StorageFile>();
foreach (string path in paths)
{
if (path != null)
files.Add(await Package.Current.InstalledLocation.GetFileAsync(path)); // << new threads
else
files.Add(null);
}
Debug.WriteLine("TextureArray.FromFilesAsync() T2 : Thread Id = " + GetCurrentThreadId());
...
}