【问题标题】:Thread order execution?线程顺序执行?
【发布时间】:2012-05-31 11:29:12
【问题描述】:

我有这个简单的代码:(我在 linqpad 中运行

void Main()
{
    for ( int  i=0;i<10;i++)
     {
      int  tmp=i;
      new Thread (() =>doWork(tmp)).Start();
     }
}

 public void doWork( int h)
 {
 h.Dump();
 }

int tmp=i; 行用于捕获变量 - 因此每次迭代都有自己的值。

2 个问题:

1)数字是顺序的,而线程执行是!

2) 有时我得到的数字少于 10 个!

这里有一些执行输出:

问题

1) 为什么会发生案例 1,我该如何解决?

2) 为什么会发生案例 2,我该如何解决?

【问题讨论】:

  • 2 很简单——在程序完成所有需要完成的工作之前不要终止程序。 (从main 返回会终止您的程序。)我不关注您对 1 的问题——您的代码中没有任何努力使它们连续,如果您希望它们是连续的,为什么要将它们分派给线程?
  • @DavidSchwartz 是的.... 1 呢?
  • 如果要按顺序执行任务,为什么要分派到不同的线程?!
  • 1 也很简单:线程启动缓慢,延迟不可预知,因此它们的实际启动顺序未定义。您必须等待第 (i-1) 个线程完成,然后才能开始第 n 个线程的工作——但这​​违背了多线程的全部目的
  • 使用线程获得并发的要点,我认为如果你想获得顺序输出,你不应该在你的任务中使用线程。但是,如果您需要在此任务中使用线程连续输出,请使用 Join() : var t = new Thread (() =>doWork(tmp)).Start(); t.Join();

标签: c# .net multithreading .net-4.0 clr


【解决方案1】:

不应期望它们是连续的。每个线程都有优先级由内核选择。可能发生它们看起来是连续的,纯粹是由每个开始时间的性质决定的,但这纯粹是机会。

为了确保它们都完成 - 将每个新线程标记为 IsBackground = false,以便它保持可执行文件处于活动状态。例如:

new Thread(() => doWork(tmp)) { IsBackground = false }.Start();

【讨论】:

  • 如果顺序很重要,请按顺序执行。如果顺序不重要,将它们分派给一堆线程。
  • @Royi:为了学习,我会推荐这个:albahari.com/threading/…
  • “线程”的定义你清楚吗?线程应该并行执行
  • @RoyiNamir 并行性的全部意义在于同时做事。哪个未排序。另外:请注意,在大多数真正的线程使用中,您不会为这样的小工作单元创建Thread。使用ThreadPool 或 4.0 中的 TPL
  • @Royi:Sleep 不保证任何事情:如果内核愿意,它可能会无限期地阻塞线程。
【解决方案2】:

线程以不可预知的顺序执行,如果主线程在其他线程之前完成,您将无法获得所有数字(dump() 将不会执行)。如果您将线程标记为 IsBackground = false,您将获得所有线程。第一个没有真正的解决方案,除了不使用线程(或加入线程,这实际上是一回事)。

【讨论】:

    【解决方案3】:

    您不应该期望线程之间有任何排序。

    如果您启动一个新线程,它只会被添加到操作系统的管理结构中。最终,线程调度程序将出现并为线程分配一个时间片。它可能会以循环方式执行此操作,随机选择一个,使用一些启发式方法来确定哪个看起来最重要(例如,拥有一个位于前台的窗口)等等。

    如果输出的顺序是相关的,您可以在之后对其进行排序,或者 - 如果您在工作开始之前就知道顺序 - 使用一个数组,其中每个线程都有一个索引,它应该将其结果写入其中。

    以您的示例的方式创建新线程也很慢。对于微任务,使用线程池至少要快一个数量级。

    【讨论】:

      【解决方案4】:

      线程管理的本质是随机的。你可以解决这两个任务,但开销太大。

      1. 问题似乎是多个线程在控制台(或您用于转储的线程)上并发,同步机制的覆盖是可能的,但很复杂,并且会导致性能下降
      2. 在调用所有线程之前退出(参见@Marc Gravell 的回答)

      【讨论】:

        【解决方案5】:

        如果排序很重要,您可能希望利用共享队列并使用信号量来确保一次只有一个线程在队列顶部运行

        【讨论】:

          【解决方案6】:

          您可以订购线程执行,但必须由您针对特定问题使用特定解决方案专门完成。

          例如:您希望线程 1、2、3 完成代码的第 1 阶段, 然后他们按照他们的 ID(您分配的这些 ID)的顺序进入下一阶段。

          您可以使用信号量来实现行为-搜索块同步和互斥以及Test-and-set方法。

          【讨论】:

            最近更新 更多