我遇到有关System.Threading
Microsoft .NET命名空间的问题。在此命名空间中,定义了许多类以帮助我管理线程。
好吧,我有一个问题,但我不知道该用什么,MSDN含糊不清,我仍然不知道哪些类做了什么。特别是,我的问题涉及同步。
我有一定数量的线程(考虑N个线程)。 在某个时刻,线程必须停止并等待至少一个其他线程做某事。 一旦N-1个线程中的一个完成了某个任务,该线程就会通知并且已停止的线程将能够继续。
所以这只是一个同步问题:线程必须等待发出信号,这就是全部。
在System.Threading
中,为了处理同步问题,提供了许多类。有WaitHandle
(s),有AutoResetEvent
(s),有ManualResetEvent
(s)等等......
我应该使用哪一个?
我的问题是:任何人都可以总结一下我应该用什么课来解决我的问题?您能告诉我们这些课程或其他课程之间最重要的区别吗?
关键在于我并没有真正理解同步问题中哪个类负责:例如,WaitHandle
和AutoResetEvent
或{{1}之间有什么区别? }?
为了处理许多线程问题,.net提供ManualResetEvent
功能和lock
类。这对夫妇能满足我的需求吗?
三江源
答案 0 :(得分:18)
Albahari's book太棒了,你应该读一段时间。它最近成长了很多!
你想要一个EventWaitHandle
(EWH),它们很好,因为没有什么可以传递,它们用于信令线程(在相同或不同的过程中),顾名思义,它们可以等待。
你会在正在进行等待的线程上打开一个,你用另一个线程将要知道的给定名称打开它。然后你等待那个等待句柄。
信令线程将打开一个同名的现有等待句柄(名称是一个字符串)并在其上调用set
。
AutoResetEvent
s和ManualResetEvent
s都继承自EWH而且他们实际上只是EWH,他们只是采取不同的行动。您想要哪一个取决于您是否希望EWH充当门或转弯风格。 如果您多次使用等待句柄或者您正在等待多个线程的等待句柄,那么您只关心这个。我使用了等待句柄(我猜)和我认为我从未使用过手册。
无论你做什么,都不要传递一个等待句柄的实例,它们应该由他们自己的线程单独打开。您指定的名称将确保它们是“相同”的等待句柄。
如果线程在不同的进程中,那么您将必须在EWH的名称前加上@"Global\"
,否则等待句柄的名称将封装在同一进程中。或者,如果您在同一个进程中使用它们,请不要使用全局命名空间。如果未指定带反斜杠的前缀,则会自动添加一个前缀以使其保密,但您不需要知道该前缀。
请注意,EWH可以获得许可,如果您遇到问题,我建议您使用EventWaitHandleRights.FullControl
,但您可以浏览完整的EventWaitHandleRights enumeration here。
我喜欢用Guid.NewGuid().ToString("N")
(Guid.NewGuid& Guid.ToString)命名我的EWH。我通常在创建信令线程时执行此操作,因为您可以在此时轻松地将信息传递给它。因此,在这种情况下,初始线程创建字符串并在创建时将其传递给信令线程。这样两个线程都知道名称,而不必进行任何花哨的跨线程传递变量。
EWH实施IDisposable
所以将其包装在using
block
EWH很好,因为如果由于某种原因信号线程打开并在等待线程甚至创建它之前发出等待句柄的信号,一切都将继续工作,等待线程将在它等待的瞬间发出信号。
因此,正在等待它的线程需要捕获一些错误,因为您需要调用OpenExisting
。如果您致电ctor
's之一并且EWH已经打开,则会按照here, under Exceptions所述引发UnauthorizedAccessException
或WaitHandleCannotBeOpenedException
。您仍然可以打开EWH并获得所需的功能,您可能只需打开它而不是创建它。
答案 1 :(得分:9)
自动重置事件和手动重置事件之间的区别在于自动重置事件在一次使用后自行清除(关闭),因此只有一个项目通过门。我怀疑 AutoResetEvent
会在这里做得很好。我个人倾向于更多地使用Monitor
- 它具有较低的开销,但你需要有点小心;你的第一个线程必须确保在任何其他线程之前拥有锁,即
object lockObj = new object();
lock(lockObj) {
// start the workers, making lockObj available to them
Monitor.Wait(lockObj);
}
与工人做类似的事情:
// lots of work
// now signal
lock(lockObj) Monitor.Pulse(lockObj);
// other work
最初持有锁 意味着我们在启动工作人员时不会错过任何消息,因为任何到达lock(lockObj)
的工作人员都将被阻止,直到原始线程释放锁定为止在Monitor.Wait
。 Pulse
的第一个线程将表示我们的原始线程继续。
答案 2 :(得分:5)
有great free e-book on this topic(并检查part 2)
关于使用什么以及什么时候,关于这个的主题有很多,比如这个:What is the difference between ManualResetEvent and AutoResetEvent in .NET?,引用Dan Goldstein:
“是的。这就像收费站和门之间的区别.PhoneResetEvent是门,需要关闭(重置).AutoResetEvent是一个收费站,允许一辆车经过并在下一个之前自动关闭一个人可以通过。“
答案 3 :(得分:1)
您可以使用AutoResetEvent
或ManualResetEvent
。唯一的区别是你是否必须自己致电Set()
或完成它。
答案 4 :(得分:0)
如果'一个N - 1线程已完成某项任务'发生在'线程必须停止并等待'到达其'某一点'之前,它会发生,还是重要?这可能会影响您选择同步。
RGDS, 马丁