所以我试图了解.net 4.5中这个新的“异步”内容。我之前使用异步控制器和任务并行库进行了一些操作,最后得到了这段代码:
采用这个模型:
public class TestOutput
{
public string One { get; set; }
public string Two { get; set; }
public string Three { get; set; }
public static string DoWork(string input)
{
Thread.Sleep(2000);
return input;
}
}
在这样的控制器中使用:
public void IndexAsync()
{
AsyncManager.OutstandingOperations.Increment(3);
Task.Factory.StartNew(() =>
{
return TestOutput.DoWork("1");
})
.ContinueWith(t =>
{
AsyncManager.OutstandingOperations.Decrement();
AsyncManager.Parameters["one"] = t.Result;
});
Task.Factory.StartNew(() =>
{
return TestOutput.DoWork("2");
})
.ContinueWith(t =>
{
AsyncManager.OutstandingOperations.Decrement();
AsyncManager.Parameters["two"] = t.Result;
});
Task.Factory.StartNew(() =>
{
return TestOutput.DoWork("3");
})
.ContinueWith(t =>
{
AsyncManager.OutstandingOperations.Decrement();
AsyncManager.Parameters["three"] = t.Result;
});
}
public ActionResult IndexCompleted(string one, string two, string three)
{
return View(new TestOutput { One = one, Two = two, Three = three });
}
由于TPL的神奇之处,此控制器以 2秒呈现视图。
现在我期望(相当天真地)使用C#5的新“异步”和“等待”功能将上面的代码转换为以下代码:
public async Task<ActionResult> Index()
{
return View(new TestOutput
{
One = await Task.Run(() =>TestOutput.DoWork("one")),
Two = await Task.Run(() =>TestOutput.DoWork("two")),
Three = await Task.Run(() =>TestOutput.DoWork("three"))
});
}
此控制器以 6秒呈现视图。在翻译的某处,代码变得不再平行。我知道异步和并行是两个不同的概念,但不知怎的,我认为代码的工作原理是一样的。有人可以指出这里发生了什么以及如何解决它?
答案 0 :(得分:18)
在翻译的某处,代码变得不再平行。
正。 await
将(异步)等待单个操作完成。
并行异步操作可以通过启动实际的Task
而不是await
来完成,直到以后:
public async Task<ActionResult> Index()
{
// Start all three operations.
var tasks = new[]
{
Task.Run(() =>TestOutput.DoWork("one")),
Task.Run(() =>TestOutput.DoWork("two")),
Task.Run(() =>TestOutput.DoWork("three"))
};
// Asynchronously wait for them all to complete.
var results = await Task.WhenAll(tasks);
// Retrieve the results.
return View(new TestOutput
{
One = results[0],
Two = results[1],
Three = results[2]
});
}
P.S。还有Task.WhenAny
。
答案 1 :(得分:7)
不,你说的原因已经不同了。并行和异步是两回事。
任务版本在2秒内工作,因为它同时运行三个操作(只要你有3个以上的处理器)。
await实际上就是这样,代码将等待Task.Run的执行,然后再继续下一行代码。
因此,TPL版本和异步版本之间的最大区别在于TPL版本以任何顺序运行,因为所有任务都是相互独立的。然而,异步版本按照编写代码的顺序运行。因此,如果您想要并行,请使用TPL,如果您想要异步,请使用async。
async的重点是能够编写同步外观代码,在长时间运行的操作发生时不会锁定UI。但是,这通常是所有处理器正在执行的操作正在等待响应。 async / await使得调用async方法的代码不会等待异步方法返回,就是这样。所以,如果你真的想用async / await模仿你的第一个模型(我会不建议),你可以这样做:
MainMethod()
{
RunTask1();
RunTask2();
RunTask3();
}
async RunTask1()
{
var one = await Task.Factory.StartNew(()=>TestOutput.DoWork("one"));
//do stuff with one
}
async RunTask2()
{
var two= await Task.Factory.StartNew(()=>TestOutput.DoWork("two"));
//do stuff with two
}
async RunTask3()
{
var three= await Task.Factory.StartNew(()=>TestOutput.DoWork("three"));
//do stuff with three
}
代码路径将是这样的(如果任务长时间运行)
**** 这是一个很大的免责声明。如果任务在await被命中时已经完成,则Await将同步运行。这节省了运行时不必执行其vudu :),因为它不需要。这将使上面的代码流不正确,因为流现在是同步的 * ** *
Eric Lippert关于此的博文解释了比我做得更好的事情:) http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx
希望这有助于消除您关于异步与TPL的一些问题?最重要的是异步不是平行的。