线程执行顺序?

5

我有这样一个简单的代码: (我在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;这一行是为了捕获变量,所以每次迭代都会有自己的值。

存在两个问题:

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

2) 有时我得到的数字比10小!

以下是一些执行输出:

enter image description hereenter image description hereenter image description hereenter image description here

问题:

1) 为什么会发生第一种情况,如何解决?

2) 为什么会发生第二种情况,如何解决?


5
2很容易——不要在程序完成所有工作之前终止它。从main返回将终止您的程序。我不明白你对1的问题——你的代码中没有任何努力使它们成为顺序执行,如果你想让它们成为顺序执行,为什么要将它们派发到线程中? - David Schwartz
@DavidSchwartz 是的....那1怎么样? - Royi Namir
1
如果您想按顺序执行任务,为什么要将它们分派到不同的线程中?! - David Schwartz
1
第一点也很简单:线程启动缓慢,延迟不可预测,因此它们实际启动的顺序是未定义的。在启动第n个线程之前,您必须等待(i-1)个线程完成工作--然而,这却破坏了多线程的整个目的。 - Vlad
1
使用线程实现并发的主要目的是提高程序效率,如果您需要按顺序输出结果,那么在此任务中不应使用线程。但如果您需要在该任务中使用线程来按顺序输出结果,请使用Join()方法:var t = new Thread (() =>doWork(tmp)).Start(); t.Join(); - Eugene Petrov
6个回答

8

不能期望线程是按顺序执行的。每个线程都被内核分配了优先级,由内核自行决定。只是因为每个线程启动的时间不同,所以看起来它们按顺序执行 可能会 发生,但这只是纯粹的机会。

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

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

2
如果顺序很重要,按顺序执行它们。如果顺序不重要,将它们分派给一堆线程。 - David Schwartz
1
@Royi:对于学习,我建议这个链接:http://www.albahari.com/threading/part2.aspx#_Signaling_with_Event_Wait_Handles - Vlad
“Thread”的定义对你来说清晰吗?线程应该是并行执行的。 - JohnB
2
@RoyiNamir 并行编程的整个重点在于同时执行任务。这是无序的。此外:请注意,在大多数实际的线程使用中,您不会为像这样的小工作单元创建一个Thread。请使用ThreadPool或4.0中的TPL。 - Marc Gravell
2
@Royi:Sleep并不能保证什么:如果内核想要的话,它可能会无限期地阻塞线程。 - Vlad
显示剩余4条评论

2

线程的执行顺序不可预测,如果主线程在其他线程之前完成,你将无法获得所有数字(dump()函数将不会执行)。如果将你的线程标记为IsBackground=false,则可以获取它们所有的结果。对于第一个问题,除了不使用线程(或者使用Join()函数,其实也是同样的方法),没有真正的解决方案。


2

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

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

如果输出的顺序很重要,您可以在之后对其进行排序,或者 - 如果您已经在工作开始之前知道顺序 - 使用一个数组,其中每个线程都被赋予一个索引,以便将其结果写入其中。

像您的示例那样创建新线程也非常缓慢。对于微任务,使用线程池至少快一个数量级。


1

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

  1. 问题在于多个线程同时访问控制台(或者你用于转储的东西),覆盖同步机制是可能的,但是复杂并且会降低性能。
  2. 在所有线程被调用之前退出(参见@Marc Gravell的答案)。

0

你可以指定线程执行顺序,但必须根据特定问题和特定解决方案进行。

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

您可以使用信号量来实现此行为-搜索块同步和互斥以及测试和设置方法。


0
如果顺序很重要,您可能需要使用共享队列并使用信号量来确保只有一个线程在队列顶部操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接