2012-04-22 372 views
7

按照MSDN Documentation TaskFactory.StartNew它创建并启动任务。因此,对于下面的代码示例为什么TaskFactory.StartNew Task没有立即启动?

class Program 
{ 
    public static void Main() 
    { 
     var t =Task.Factory.StartNew(
       () => SomeLongRunningCalculation(10, Display) 
      ); 
     var t1 = Task.Factory.StartNew(
       () => SomeLongRunningCalculation(20, Display) 
      ); 
     Console.WriteLine("Invoked tasks"); 
     Task.WaitAll(t, t1); 
     Console.ReadLine(); 
    } 

    public static void Display(int value) 
    { 
     Console.WriteLine(value); 
    } 

    public static void SomeLongRunningCalculation(int j, Action<int> callBack) 
    { 
     Console.WriteLine("Invoking calculation for {0}", j); 
     System.Threading.Thread.Sleep(1000); 
     if (callBack != null) 
     { 
      callBack(j + 1); 
     } 
    } 
}  

我预计产量为

Invoking calculation for 10 
Invoking calculation for 20 
Invoked tasks 
11 
21 

但是,它显示

Invoked tasks 
Invoking calculation for 20 
Invoking calculation for 10 
21 
11 

我想学习

  1. 为什么StartNew后任务没有立即运行?
  2. 我该怎么做才能以预期的格式获得输出?

回答

10

这是使用单核CPU的机器上很有可能出现的结果。或者可能有一个多核CPU,它也忙于做其他事情。

创建一个任务或线程只会设置一个允许代码运行的逻辑操作系统结构。操作系统调度程序不是如果内核繁忙,则立即开始执行它,线程必须与机器上运行的所有其他线程竞争。一个典型的Windows会话有一千个左右。每秒64次,内核产生一个中断,调度器重新评估正在发生的事情,看看另一个线程是否应该轮到。任何未被阻塞的线程(等待其他线程完成像读取文件或网络包一样的工作)都有资格运行,并且调度程序选择具有最高优先级的线程。调度程序中的一些额外代码使用优先级值确保所有线程都有机会。

机会是关键词。线程调度是非确定性

请注意,我从来没有说过有关线程或任务或ThreadPool类的任何内容。他们没有权力对操作系统调度线程的方式做任何事情。所有这一切都可能是阻止一个线程从运行,线程池调度程序的工作。

优先事项,你可以修改Thread.Priority或Task.Priority属性来影响结果。但是,没有砰的一声,操作系统调度程序会不断调整线程的优先级,使其根据您使用该属性设置的基本优先级进行调整。例如,您无法阻止有史以来的线程以另一个具有较高优先级的方式运行。

希望线程以可预测的顺序运行导致最糟糕的错误,一个线程比赛错误。第二糟糕是僵局。他们非常难以调试,因为他们依赖于时间和可用的机器资源和负载。您只能通过编写明确照顾它的代码来确保您获得特定订单。你通过使用像Mutex这样的线程原语或关键字。值得注意的是,当你试图将这些代码添加到你的代码片段中时,你最终会得到一个不再具有并发性的程序。换句话说,你将最终没有任何用于任务的任务了。一个线程或任务只会在可以让负担,让它在不可预知的时间运行。

+0

+1表示“线程调度是非确定性的”。 – Ramesh 2012-04-23 02:06:38

7

为什么StartNew后没有立即运行任务?

关于MSDNStartNew()时间表执行的任务。

调用StartNew在功能上等同于创建一个使用它的构造函数的 一个任务,然后调用开始以安排 执行。但是,除非创建和调度必须分开,否则对于简化和 性能而言,建议使用StartNew。

由于TPL使用ThreadPool线程 - 有时它必须做一些工作才能为特定的任务执行保留并启动一个ThreadPool线程。如果你需要明确地运行一个单独的线程,而不需要像TPL的TaskScheduler这样的中间机制 - 手动创建并启动一个线程,并且你不会有像延续这样的整洁的东西。

0

请注意,我从来没有对线程或任务或线程池 类有任何表示。他们没有权力对操作系统调度线程的方式做任何事情。所有这一切都可以阻止 运行一个线程,这是线程池调度程序的工作。

战争......任务与线程......这取决于所需的任务。

对于这个示例,我们需要从100个同时执行的任务(一个客户端执行一个任务)中为100个客户端(一个客户端分配一个图块)加载来自Internet的100张图片。而且我们有一个共同的时间限制,一些加载任务的加载时间可以与通常的时间限制重叠。 简单测试表明,在执行100个任务(类任务)的同时,在有限的时间内同时执行100个线程(类线程),效率更高。 10个线程与10个任务的结果相同。 我的意思是说,如果我们需要的不仅仅是“一些呆滞”,而是强健的任务,即在同时执行的任务中做更多的工作,那么我们应该使用类Thread。

相关问题