线程和任务调度

3

我有一个通过AutoResetEvent同步线程的代码。

基本上有两个线程交替控制并执行命令,每次只能有一个线程执行。

代码

static EventWaitHandle _waitHandle = new AutoResetEvent(false);
static void Waiter()
{

    _waitHandle.WaitOne();
    Console.WriteLine("A...");
    _waitHandle.Set();  
    _waitHandle.WaitOne();
    Console.WriteLine("A2...");
    _waitHandle.Set();
}

static void Waiter2()
{   
    _waitHandle.WaitOne();
    Console.WriteLine("B...");
    _waitHandle.Set();
    _waitHandle.WaitOne();
    Console.WriteLine("B2...");
}


void Main()
{
    new Thread(Waiter).Start();
    new Thread(Waiter2).Start();
   _waitHandle.Set(); // Wake up the Waiter.
}

结果:(我总是得到这个结果)

A...
B...
A2...
B2...

然而,当我转移到 任务 时:
Task.Run(()=>Waiter());
Task.Run(()=>Waiter2());

我有时会遇到以下问题:

B...
A...
B2...

我明白这是因为任务调度器安排了第二个任务先执行。

这让我有一个问题:

问题

1)线程的顺序是否保证与调用顺序相同:

new Thread(Waiter).Start();
new Thread(Waiter2).Start();
//In other words , will I always get the first result ?

2) 我如何强制执行 Task.Run 的顺序与我调用它们的顺序相同?


执行顺序不能保证。此外,让多个线程等待相同的“AutoResetEvent”是一个非常糟糕的想法,需要认真考虑...这篇文章是一篇很好的阅读材料。链接 - huysentruitw
@WouterHuysentruit 谢谢。 - Royi Namir
2个回答

3
没有同步机制,无法保证线程启动和/或执行的顺序。此外,线程的执行可能随时被抢占(想一下:“暂停”)。 因此,回答你的问题:1. 不是,2. 不是。在继续之前,应该问自己“我真的需要使用线程来解决这个问题吗?” 我最喜欢的一句话来自微软的MSDN: “当您使用任何形式的多线程时,您可能会面临非常严重和复杂的错误”[实现基于事件的异步模式的最佳实践] 如果确实需要引入线程,则应首先熟悉一些Microsoft的同步机制。

关键区段在这里有什么关系?此外,我不明白你的旁白部分如何有助于你的回答。并不是我不熟悉这个主题。我只是有一个调度问题。就这些 :-) - Royi Namir
你的原始问题涉及到线程同步机制。临界区是其中一个例子。 - Pressacco
我不同意。锁定确保在共享代码部分只有一个线程。我没有遇到这个问题。但无论如何... - Royi Namir

3
  1. 不,这并不是保证的,你只是幸运地得到了每次输出都相同的结果。
  2. 添加第二个 AutoResetEvent,在两个任务之间加入 WaitOne,并在 Waiter 方法开始处进行 Set

Scott,假设我有10个线程而不是2个,我想要同样的东西-我必须有20个等待句柄吗? - Royi Namir
不,你只需要在每个方法之间加上WaitOne,并在每个方法的开头使用与第一个WaitOne相同的AutoResetEvent进行Set。 - Scott Chamberlain

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