2015-12-30 26 views
-1

我做了一个样本使用下面的代码来模拟并发:“新主题”和异步

var threads = new Thread[200]; 
//starting threads logic 
for (int i = 0; i < 200; i++) 
     { 
      threads[i].Start(); 
     } 
     for (int i = 0; i < 200; i++) 
     { 
      threads[i].Join(); 
     } 

的代码应该插入大量的记录到数据库中,它似乎运作良好,因为线程几乎在同一时间完成。

但是,当我使用:

var tasks = new List<Task<int>>(); 
     for (int i = 0; i < 200; i++) 
     { 
      tasks.Add(insert(i)); 
      // await insert(i); 
     } 
     int[] result = await Task.WhenAll(tasks); 

需要花费大量的时间来完成相同的逻辑。

有人可以向我解释有什么区别吗?我认为Await应该创建线程。

+4

编号await不会创建线程。 http://blog.stephencleary.com/2012/02/async-and-await.html在这种情况下,Async与多线程完全无关。 – Aron

+0

在代码的哪里你开始*你的任务? – Fabjan

+0

您是否知道启动一个线程会消耗超过1MB的内存?所以200远远超过200MB。这没有做任何处理。起始线程很慢。从线程池中使用它们要好得多。 – Enigmativity

回答

1

如果您需要复制基于原始Thread的行为,则可以使用Task.Factory.StartNew(... , TaskCreationOptions.LongRunning)来安排工作,然后阻止,直到通过Task.WaitAll完成工作人员任务。我不推荐这种方法,但就行为而言,这将与您的代码以前的工作方式非常接近。

更深入的分析,为什么会没有得到您的方案的预期性能如下:

说明,第1部分async并不意味着“在不同的线程”)

标有async关键字的方法不会奇迹般地异步运行。它们仅仅是能够将等待操作(可以或不可以自己异步运行)组合为单个较大单元(通常为TaskTask<T>)的能力能力

如果您insert方法async,它仍然是有可能,它至少执行工作的一些同步。 全部您的代码在第一个await声明之前,肯定会出现这种情况。这项工作将在“main”线程(调用insert的线程)上执行 - 这将成为您的瓶颈或至少是其中的一部分,因为您的代码段的并行度将为1,而您在调用insert紧密的循环,不管你是否由于await而产生的任务。

为了说明上述一点,考虑以下示例:

void Test() 
{ 
    Debug.Print($"Kicking off async chain (thread {Thread.CurrentThread.ManagedThreadId}) - this is the main thread"); 
    OuterTask().Wait(); // Do not block on Tasks - educational purposes only. 
} 

async Task OuterTask() 
{ 
    Debug.Print($"OuterTask before await (thread {Thread.CurrentThread.ManagedThreadId})"); 
    await InnerTask().ConfigureAwait(false); 
    Debug.Print($"OuterTask after await (thread {Thread.CurrentThread.ManagedThreadId})"); 
} 

async Task InnerTask() 
{ 
    Debug.Print($"InnerTask before await (thread {Thread.CurrentThread.ManagedThreadId})"); 
    await Task.Delay(10).ConfigureAwait(false); 
    Debug.Print($"InnerTask after await (thread {Thread.CurrentThread.ManagedThreadId}) - we are now on the thread pool"); 
} 

这将产生以下输出:

 
Kicking off async chain (thread 6) - this is the main thread 
OuterTask before await (thread 6) 
InnerTask before await (thread 6) 
InnerTask after await (thread 8) - we are now on the thread pool 
OuterTask after await (thread 8) 

注意,第一await内部Task1甚至Task2之前的代码仍然执行在“主”线程上。我们的链实际上是在同一个线程中同步执行的,这个线程启动了外部任务,直到我们await第一次真正的异步操作(在这种情况下为Task.Delay)。

此外

如果您在SynchronizationContext.Current不为空(即Windows窗体,WPF)的环境中运行并且你不使用你的insert方法内等待的任务ConfigureAwait(false),然后延续计划通过异步状态机第一个await声明将可能在“主”线程上执行 - 虽然这在某些环境(即ASP.NET)中不能保证。

说明,部分2(执行Task S于线程池)

如果像你insert方法的一部分,你都选择手动启动任何Task S,那么你最有可能安排你的工作在线程池中使用Task.Run任意其他启动未指定​​的新任务的方法。一旦线程池饱和,任何新启动的任务将排队,从而降低并行系统的吞吐量。

证明:

IEnumerable<Task> tasks = Enumerable 
    .Range(0, 200) 
    .Select(_ => Task.Run(() => Thread.Sleep(100))); // Using Thread.Sleep to simulate blocking calls. 

await Task.WhenAll(tasks); // Completes in 2+ seconds. 

现在用​​:

IEnumerable<Task> tasks = Enumerable 
    .Range(0, 200) 
    .Select(_ => Task.Factory.StartNew(
     () => Thread.Sleep(100), TaskCreationOptions.LongRunning 
    )); 

await Task.WhenAll(tasks); // Completes in under 130 milliseconds. 

它一般是产卵200个线程(这不会很好地扩展)一个好主意,但如果大规模阻塞调用的并行化是一个绝对的要求,上面的代码片段向您展示了一种使用TPL实现的方法。

+0

谢谢,真的你的回复是有帮助的 –

0

在第一个示例中,您手动创建了线程。第二你创建任务。任务 - 可能 - 正在使用线程池,线程数量有限。所以,大部分任务都在队列中等待,而其中很少任务在可用线程上并行执行。

+0

任务(等待/异步)[不要创建或使用theads](http://blog.stephencleary.com/2013/11/there-is-no-thread.html),它只是一个非常复杂的状态机。 –

+0

@Erik当然,但任务正在使用线程执行。 TaskSheduker管理任务,并使用线程池中的线程执行它们。 https://msdn.microsoft.com/en-us/library/dd997402%28v=vs.110%29.aspx – codefox

+0

谢谢你们,真的你的回复有帮助 –