C#异步 - 这里创建了多少个对象?

时间:2016-02-15 13:13:54

标签: c# multithreading asynchronous

我想了解我写的代码,

for (int i = 0; i< 5; i++)
{
    ExecuteCMD("/c robocopy C:\\Source D:\\Source /MIR", true);
}

public async void ExecuteCMD(string cmdLine, bool waitForExit)
{
    await Task.Factory.StartNew(() => 
    {
        ExecuteProcess proc = new ExecuteProcess(cmdLine, waitForExit);
        proc.Execute();
    } );
}

异步方法ExecuteCMD将循环运行5次。我知道async不会创建新线程。那么,是否在同一个线程中创建了5个具有相同名称(&#39; proc&#39;)的对象?请解释

非常感谢提前!

2 个答案:

答案 0 :(得分:2)

你的意思是你的ExecuteProcess proc对象?这是lambda函数的局部变量。所以你的代码没有冲突。

The Lambda

() => 
{
    ExecuteProcess proc = new ExecuteProcess(cmdLine, waitForExit);
    proc.Execute();
}

被称为5次,但每次调用只为变量ExecuteProcess创建proc的一个实例。

答案 1 :(得分:2)

您正在使用Task.Factory.StartNew,因此您很可能(请参阅Stephen Cleary的评论)最终会出现在默认TaskScheduler上,这恰好会在线程池线程上执行。因此,您的ExecuteProcess分配和Execute调用将在线程池线程上发生5次(如上所述,请参见上面的点默认调度程序) - 并且很可能彼此并行,和< / em>与你的for循环并行(这最后一部分可能很难解决,但这是async void的整个问题 - 执行顺序是非确定性的;稍后会详细介绍)。

你是有点,因为async/await不一定会创建新线程。 async/await是关于链接任务及其延续的全部内容,以便它们以相互正确的顺序执行。实际Task次运行的位置取决于Task的创建方式。在这里,您显式请求将您的工作推送到线程池,因为这是由Task执行的Task.Factory.StartNew执行的地方。

其他人指出你可能错误地使用async void - 可能是由于缺乏理解。 async void仅适用于以一种即发即弃的方式安排工作,并且这项工作需要是自包含的,并且具有自己的异常处理和并发控制。因为您的async void将无法观察,并行将运行到其余代码中。它就像是说:“我希望这段代码在将来的某个时刻运行。我不需要知道它何时完成或是否引发了任何例外 - 我只是等到它碰到第一个{{1然后继续执行我自己的工作 - await的其余部分将独立进行,无需监督“。因此,如果您在 async void循环之后放置一些代码,那么很可能会在 for工作之前执行,这可能会或可能会不是你想要的。

以下是您的应用程序实际发生情况的逐步视图。

您在主线程上遇到了ExecuteProcess循环的第一次迭代。运行时调用for,就像它是任何其他同步方法调用一样。它进入方法并在主线程上执行代码前面的 ExecuteCMD,仍然是第一个await循环迭代的一部分。然后它通过for在线程池上安排一些工作。这项工作将在未来的某个时刻执行,比方说,线程池线程#1。然后等待Task.Factory.StartNew返回的Task。此任务不可能同步完成,因此Task.Factory.StartNew 计划将来运行的await的其余部分(在主线程上,{{1创建的任务之后)已完成)和收益率。此时你的async void工作可能还没有开始,但主线程已经可以自由跳转到Task.Factory.StartNew循环的第二次迭代。它正是如此,这导致另一个任务被安排在线程池线程#2上运行,在未来的某个时刻 - 然后是另一个计划在主线程上运行的延续。然后ExecuteProcess循环跳转到下一个项目。

当你的for循环结束时,你很可能有5 for个等待在线程池线程上执行,然后是5个继续for等待在主线程。它们将在未来的某个时刻完成。您的代码不会知道何时因为您告诉它您不关心(使用Task)。