MVC4 异步和并行执行
因此,我正在尝试了解 .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 });
}
这个控制器在2秒内渲染视图,感谢 TPL 的魔力。
现在,我期望(相当天真地)使用 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 秒内呈现视图。在翻译的某个地方,代码变得不再并行。我知道异步和并行是两个不同的概念,但不知何故我认为代码的工作原理是相同的。有人可以指出这里发生了什么以及如何解决它吗?
So I'm trying to get my head around this new 'async' stuff in .net 4.5. I previously played a bit with async controllers and the Task Parallel Library and wound up with this piece of code:
Take this model:
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;
}
}
Which is used in a controller like this:
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 });
}
This controller renders the view in 2 seconds, thanks to the magic of the TPL.
Now I expected (rather naively) that the code above would translate into the following, using the new 'async' and 'await' features of 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"))
});
}
This controller renders the view in 6 seconds. Somewhere in the translation the code became no longer parallel. I know async and parallel are two different concepts, but somehow I thought the code would work the same. Could someone point out what is happening here and how it can be fixed?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
恰恰。
await
将(异步)等待单个操作完成。并行异步操作可以通过启动实际的
Task
来完成,但稍后才await
它们:PS还有一个
Task.WhenAny
。Precisely.
await
will (asynchronously) wait for a single operation to complete.Parallel asynchronous operations can be done by starting the actual
Task
s but notawait
ing them until later:P.S. There's also a
Task.WhenAny
.不,你已经说明了这是不同的原因。并行和异步是两个不同的东西。
Task 版本的工作时间为 2 秒,因为它同时运行三个操作(只要您有 3 个以上的处理器)。
实际上,await 顾名思义,代码将等待 Task.Run 的执行,然后再继续执行下一行代码。
因此,TPL 版本和异步版本之间的最大区别在于,TPL 版本可以任意顺序运行,因为所有任务都是相互独立的。而异步版本则按照代码编写的顺序运行。因此,如果您想要并行,请使用 TPL,如果您想要异步,请使用异步。
异步的要点是能够编写看起来同步的代码,在发生长时间运行的操作时不会锁定 UI。然而,这通常是处理器正在执行的所有操作都在等待响应的操作。 async/await 使得调用异步方法的代码不会等待异步方法返回,仅此而已。因此,如果您确实想使用 async/await 来模拟您的第一个模型(我不建议),您可以这样做:
代码路径将像这样(如果任务是长时间运行)
**** 不过,对此有一个很大的免责声明。如果在点击等待时任务已经完成,则等待将同步运行。这使得运行时不必执行 vudu :),因为不需要它。这将使上面的代码流不正确,因为该流现在是同步的****
Eric Lippert 的博客文章对此进行了解释事情比我现在做的好多了:)
<一href="http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx ">http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx
希望这有助于消除您关于异步与 TPL 的一些问题?最重要的是异步不是并行。
No, you stated the reason that this is different already. Parallel and Async are two different things.
The Task version works in 2 seconds because it runs the three operations at the same time (as long as you have 3+ processors).
The await is actually what it sounds like, the code will await the execution of the Task.Run before continuing to the next line of code.
So, the big difference between the TPL version and the async version are that the TPL version runs in any order because all of the tasks are independent of each other. Whereas, the async version runs in the order that the code is written. So, if you want parallel, use the TPL, and if you want async, use async.
The point of async is the ability to write synchronous looking code that will not lock up a UI while a long running action is happening. However, this is typically an action that all the processor is doing is waiting for a response. The async/await makes it so that the code that called the async method will not wait for the async method to return, that is all. So, if you really wanted to emulate your first model using async/await (which I would NOT suggest), you could do something like this:
The code path will go something like this (if the tasks are long running)
****A big disclaimer about this, though. Await will run synchronously if the task is already completed by the time that the await is hit. This saves the runtime from having to perform its vudu :) since it is not needed. This will make the code flow above incorrect as the flow is now synchronous****
Eric Lippert's blog post on this explains things much better than I am doing :)
http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx
Hopefully, that helps dispel some of your questions about async versus TPL? The biggest thing to take away is that async is NOT parallel.