2012-01-30 69 views
12

所以我试图让我的头在.net 4.5中这个新的'异步'的东西。我先前播放一点与异步控制器和任务并行库,并与这一段代码卷起:MVC4异步和并行执行

借此模型:

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

所以基本上我混淆了异步和平行。我(错误地)认为当一个任务的结果最终被使用时会发生实际的等待(与linq查询不同,只有当它被枚举时才会执行)。 – Lodewijk 2012-01-31 12:08:40

回答

18

在翻译的某处,代码变得不再平行。

准确地说。 await将(异步)等待单个操作完成。

并行异步操作可以通过启动实际Task s内完成,但不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] 
    }); 
} 

附:还有一个Task.WhenAny

+0

好吧,我明白异步和并行(我自己在提问时猜测它自己)之间的区别,但是它是否等同于Task.WhenAll的结果?如果控制器中的操作已经是“异步”,那么在异步方法内部等待会不会对系统造成额外的负担? – Lodewijk 2012-01-31 12:03:14

+0

是的,这是必要的,因为在构建视图之前需要完成任务。使用异步等待比同步阻止更少的负担 - 它与旧的“AsyncManager”方法大致相同。 – 2012-01-31 13:24:49

+0

重要一点:'async'关键字除了允许在该方法中使用“await”关键字外,什么也不做。这是'await'关键字 - 而不是'async'关键字 - 使事物异步。所以你应该在每个'async'方法的某个地方有一个'await'(如果你不这样做,编译器会发出警告)。 – 2012-01-31 13:26:24

5

不,你说明了这是不同的原因。并行和异步是两个不同的东西。

任务版本在2秒内工作,因为它同时运行三个操作(只要您有3个以上的处理器)。

await实际上就是它听起来的样子,代码将在继续执行下一行代码之前等待Task.Run的执行。

因此,TPL版本和异步版本之间的巨大差异在于TPL版本以任意顺序运行,因为所有任务都是相互独立的。而异步版本按照代码编写的顺序运行。所以,如果你想并行,使用TPL,如果你想异步,使用异步。

异步的要点是能够编写同步代码,在长时间运行的操作发生时不会锁定UI。但是,这通常是所有处理器正在等待响应的动作。异步/等待使得调用异步方法的代码不会等待异步方法返回,就是这样。所以,如果你真的想使用异步效仿你的第一个模型/等待(我会建议),你可以做这样的事情:

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 
} 

的代码路径会是这样的(如果任务是长时间运行)

  1. 主要调用RunTask1
  2. RunTask1等待,并返回
  3. 到RunTask2
  4. RunTa主通话SK2等待并返回
  5. 到RunTask3主呼叫
  6. RunTask3等待,并返回
  7. 主要是现在做
  8. RunTask1/2/3将返回并继续做的事情有一个/两/三
  9. 同7除了小于已经完成
  10. 同7,除了二少一个已完成

****尽管这是一个很大的声明。如果在等待被击中的时候任务已经完成,等待将同步运行。这可以节省运行时间,因为它不是必需的,所以不必执行它的vudu :)。这将使上述不正确的代码流的流量现在是同步* ** *

埃里克利珀对本博客文章解释事情变得更好,比我做的:) http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx

希望这有助于消除一些关于异步与TPL的问题?最大的缺点是异步不是平行的。

+1

我喜欢你的答案,但我接受了另一个,因为它包含了我的示例的可行版本。 – Lodewijk 2012-01-31 15:33:58