我有这个简单的代码:(我在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正在发生,我该如何解决?
答案 0 :(得分:8)
不应期望它们是连续的。每个线程都会在内核选择时获得优先级。它可能发生它们看起来是连续的,完全取决于每个开始时的性质,但这是纯粹的机会。
为了确保它们全部完成 - 将每个新线程标记为IsBackground = false
,以便使可执行文件保持活动状态。例如:
new Thread(() => doWork(tmp)) { IsBackground = false }.Start();
答案 1 :(得分:2)
线程以不可预测的顺序执行,如果主线程在其他线程之前完成,则不会获得所有数字(dump()将不会执行)。如果您将线程标记为IsBackground = false,那么您将全部获得它们。第一个没有真正的解决方案,除了不使用线程(或连接线程,这实际上是相同的)。
答案 2 :(得分:2)
你不应该期望线程之间有任何顺序。
如果您启动一个新线程,它只会添加到操作系统的管理结构中。最终,线程调度程序将出现并为线程分配时间片。它可以以循环方式执行此操作,选择随机方式,使用一些启发式方法来确定哪一个看起来最重要(例如,拥有位于前台的Window的一个)等等。
如果输出的顺序是相关的,你可以在之后对其进行排序,或者 - 如果你知道工作开始之前的排序 - 使用一个数组,其中给每个线程一个索引,它应该写入它的结果。
以您的示例的方式创建新线程也非常慢。对于微任务,使用线程池的速度至少要快一个数量级。
答案 3 :(得分:1)
线程管理的本质是随机的。你可以解决这两个任务,但开销太大了。
答案 4 :(得分:0)
如果排序很重要,您可能希望利用共享队列并使用信号量来确保一次只有一个线程在队列顶部运行
答案 5 :(得分:0)
您可以订购线程执行,但必须由您针对特定解决方案的特定问题专门完成。
例如:您希望线程1,2,3完成您的代码的第1阶段, 然后他们按照ID的顺序进入下一阶段(这些ID已分配)。
您可以使用信号量来实现此行为 - 搜索块同步和互斥以及测试和设置方法。