.NET Framework中的同步原语:哪一个是好的原语?

And*_*dry 21 .net c# multithreading synchronization signals

我有一个关于System.ThreadingMicrosoft .NET命名空间的问题.在此命名空间中,定义了许多类以帮助我管理线程.好吧,我有一个问题,但我不知道该用什么,MSDN含糊不清,我仍然不知道哪些类做了什么.特别是,我的问题涉及同步.

问题

我有一定数量的线程(考虑N个线程).在某个时刻,线程必须停止并等待至少一个其他线程做某事.一旦N-1个线程中的一个完成了某个任务,该线程就会通知并且已停止的线程将能够继续.

所以这只是一个同步问题:线程必须等待发出信号,这就是全部.

很多班

System.Threading提供许多类以处理同步问题.有WaitHandle(s),有AutoResetEvent(s),有ManualResetEvent(s)等...

我应该使用哪一个?

这个问题

我的问题是:任何人都可以总结一下我应该用什么课来解决我的问题?您能告诉我们这些课程或其他课程之间最重要的区别吗?

问题的关键是,我没有带真正理解类负责在同步事:有什么区别,例如,与WaitHandleAutoResetEventManualResetEvent

锁怎么样?

为了处理许多线程问题,.net提供了lock功能和Monitor类.这对夫妇对我的需求有益吗?

谢谢

All*_*ice 19

阿尔巴哈里的书太棒了,你应该读一段时间.它最近成长了很多!

你想要什么

你想要一个EventWaitHandle(EWH),它们很好,因为没有什么可以传递,它们用于信令线程(在相同或不同的过程中),顾名思义,它们可以等待.

你是如何使用它的

你可以在正在进行等待的线程上打开一个,用另一个线程将要知道的给定名称打开它.然后你等待那个等待句柄.

信令线程将打开一个同名的现有等待句柄(名称是一个字符串)并调用set它.

差异

AutoResetEventsManualResetEvents都继承自EWH,他们实际上只是EWH,他们只是采取不同的行动.您想要哪一个取决于您是否希望EWH充当门或转弯风格. 如果您多次使用等待句柄或者您正在等待多个线程的等待句柄,您只关心这一点. 我已经使用了等待处理量(我想)并且我认为我从未使用过手册.

重要的是要知道

  • 无论你做什么,都不要传递一个等待句柄的实例,它们应该由他们自己的线程单独打开.您指定的名称将确保它们是"相同"的等待句柄.

  • 如果线程在不同的进程中,那么您将必须为EWH的名称添加前缀@"Global\",否则等待句柄的名称将封装在同一进程中.或者,如果您在同一个进程中使用它们,请不要使用全局命名空间.如果未指定带反斜杠的前缀,则会自动添加一个前缀以使其保持私有状态,但您不需要知道该前缀.

  • 请记住,EWH可以获得许可,如果您遇到问题,我建议您使用EventWaitHandleRights.FullControl,但您可以在此处浏览完整的EventWaitHandleRights枚举.

  • 我喜欢用Guid.NewGuid().ToString("N")(Guid.NewGuidGuid.ToString)命名我的EWH.我通常在创建信令线程时执行此操作,因为您可以在此时轻松地将信息传递给它.因此,在这种情况下,初始线程创建字符串并在创建时将其传递给信令线程.这样两个线程都知道名称,而不必进行任何花哨的跨线程传递变量.

  • EWH实现IDisposable所以将其包装在一个using块中

比赛条件

EWH很好,因为如果由于某种原因信号线程打开并在等待线程创建它之前发出等待句柄的信号,一切都将继续工作,等待线程将在它等待的瞬间发出信号.

因此,正在等待它的线程需要有一些错误捕获因为你需要调用OpenExisting.如果调用的一个ctor和EWH已经打开,你会得到一个UnauthorizedAccessExceptionWaitHandleCannotBeOpenedException描述扔在这里,下例外.您仍然可以打开EWH并获得所需的功能,您可能只需打开它而不是创建它.

  • @allenrice,只有未命名的互斥锁是进程的本地.任何命名的互斥锁都是系统范围的.添加`@"Global \\"`使它在终端服务器会话之间变得全局(即每个人都可以看到它),添加`Local \\`或者什么都不使它只对当前会话可见.所以在这方面它有点不同. (3认同)

Mar*_*ell 9

自动重置事件和手动重置事件之间的区别在于自动重置事件在一次使用后自行清除(关闭),因此只有一个项目通过门.我怀疑AutoResetEvent会很好.我个人倾向于使用Monitor更多 - 但它具有较低的开销,但你需要有点小心; 你的第一个线程必须确保在任何其他线程之前拥有锁,即

object lockObj = new object();
lock(lockObj) {
    // start the workers, making lockObj available to them

    Monitor.Wait(lockObj);
}
Run Code Online (Sandbox Code Playgroud)

与工人做类似的事情:

// lots of work
// now signal
lock(lockObj) Monitor.Pulse(lockObj);
// other work
Run Code Online (Sandbox Code Playgroud)

最初持有锁意味着我们在启动工作时不会丢失任何消息,因为任何工作人员lock(lockObj)都会被阻塞,直到原始线程释放锁定为止Monitor.Wait.第一个线程Pulse将指示我们的原始线程继续.


Den*_*dic 5

关于这个主题有一本很棒的免费电子书(并且检查第2部分)

对于使用什么以及什么时候,在SO上有很多关于这个的主题,比如这个:.NET中的ManualResetEvent和AutoResetEvent有什么区别?,引用Dan Goldstein的话:

"是的.这就像收费站和门之间的区别.手动重置是门,需要关闭(重置).AutoResetEvent是一个收费站,允许一辆车经过并自动关闭,然后下一个可以得到通过."