【问题标题】:Synchronization primitives in the .NET Framework: which one is the good one?.NET Framework 中的同步原语:哪个好?
【发布时间】:2011-09-16 07:27:13
【问题描述】:

我对@9​​87654323@ Microsoft .NET 命名空间有疑问。在这个命名空间中,定义了许多类以帮助我管理线程。 好吧,我有一个问题,但我不知道该使用什么,MSDN 含糊不清,我仍然不知道什么类做什么。特别是,我的问题与同步有关。

问题

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

所以这只是一个同步问题:线程必须等待发出信号,仅此而已。

许多类

System.Threading 中提供了许多类来处理同步问题。有WaitHandle(s),有AutoResetEvent(s),有ManualResetEvent(s)等等……

我该用哪一个?

问题

我的问题是:谁能总结一下我应该使用哪个课程来解决我的问题?您能说出这些类或其他类之间最重要的区别吗?

关键是我还没有真正理解在同步问题中哪个类负责:例如,WaitHandleAutoResetEventManualResetEvent 之间有什么区别?

锁呢?

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

谢谢

【问题讨论】:

  • 您可能会发现Task Parallel Library 很有用,具体取决于。
  • @Ozair 该帖子是关于特定通知的,并不总是最好的。请参阅 Marcs 的回答并注意有关“缺少消息”的备注。
  • BTW AutoResetEventManualResetEvents 都是 WaitHandles,它们只是包装了 WaitHandle 的 ctor 并将不同的参数传递给它,因此它将是自动重置或手动重置。无论哪种方式,您都在使用等待句柄,这大大简化了您需要查找的信息量
  • 此外,当您说线程必须发出信号时,请记住,我们假设您此时不需要传递任何信息,只需发出信号以继续。如果该假设不正确,请改写问题:)

标签: c# .net multithreading synchronization signals


【解决方案1】:

Albahari's book 太棒了,你应该花点时间仔细阅读它。最近长大了很多!

你想要什么

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

你如何使用它

您将在正在等待的线程上打开一个,您使用另一个线程将知道的给定名称打开它。然后你在那个等待句柄上等待。

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

区别

AutoResetEventsManualResetEvents 都继承自 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 已经打开,您将收到UnauthorizedAccessExceptionWaitHandleCannotBeOpenedException,如here, under Exceptions 所述。您仍然可以打开该 EWH 并获得所需的功能,您可能只需要打开它而不是创建它。

【讨论】:

  • 你让我大开眼界!!! EventWaitHandle(s) 太棒了!!!所以只是名字是必要的我不需要传递引用.....是正确的????????????
  • 没错,不要把它们传过来,呵呵,关键是你按名字打开它们,没有传球。它们真的很棒,因为它们非常易于使用,而且概念非常简单。有一些警告,但我已经涵盖了我遇到的所有警告:)
  • 再问一个问题,但是您已经得到了正确的答案,EWH 是应用程序和系统范围的,但是如果我不放置全局命名空间的东西,我确信那些 etws 不会被其他应用程序访问对吗??????互斥量呢???这只是为了我的个人信息:MSDN 说 Mutex 是特定于应用程序范围的原因,所以它们已经带有 gloab 命名空间??????为了简单起见:如果我实例化一个互斥锁,其他人也可以使用它吗????非常感谢
  • 对,没有全局命名空间,你根本无法从另一个进程访问这个 EWH。我不确定Mutex,我从未使用过它,因为我从未遇到过简单的信号情况,我觉得需要任何功能或速度超过 EWH 提供的功能。在 MSDN 上,他们的互斥锁示例说它是本地互斥锁,在该示例中,他们不使用 @"Global\" 作为前缀,所以我假设规则与 EWH 相同。至于Mutex 的可访问性,我不知道,对不起。
  • @allenrice,只有未命名的互斥锁是进程本地的。任何命名的互斥锁都是系统范围的。添加@"Global\\" 使其在终端服务器会话之间成为全局(即每个人都可以看到它),添加`Local\` 或不添加任何内容使其仅对当前会话可见。所以它在这方面有点不同。
【解决方案2】:

自动重置事件和手动重置事件之间的区别在于,自动重置事件在使用一次后会自行清除(关闭),因此只有一件物品可以通过大门。我怀疑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

持有锁原本意味着我们在启动worker时不会错过任何消息,因为任何到达lock(lockObj)的worker都会被阻塞,直到原始线程在@释放锁987654326@。 Pulse 的第一个线程将向我们的原始线程发出信号以继续。

【讨论】:

  • +1 表示工作线程可能在构造线程到达 wait() 之前到达屏障。
  • “清除自身”一词是“无信号”或“无信号”(与 Windows 中的所有可等待对象一样,它们要么有信号,要么无信号)。
  • @Andry 当然!等待和脉冲/脉冲全部。调用wait时,你放弃锁并加入等待队列;对 Pulse 的调用将一个线程从等待队列移动到就绪队列(PulseAll 移动所有线程);当脉冲线程释放锁时,原来等待的线程可以获得它,等等
【解决方案3】:

有一个great free e-book on this topic(并检查part 2

关于使用什么以及何时使用,SO 上有很多关于此的主题,例如:What is the difference between ManualResetEvent and AutoResetEvent in .NET?,引用 Dan Goldstein:

“是的。就像收费站和门的区别。ManualResetEvent 是门,需要关闭(重置)。AutoResetEvent 是收费站,允许一辆车经过并在下一辆车前自动关闭一个人可以通过。”

【讨论】:

  • 是的,在我阅读了免费版本后,我立即去亚马逊买了一本 :) 虽然,老实说,这本书最好的部分是关于线程的......
【解决方案4】:

您可以使用AutoResetEventManualResetEvent。唯一的区别是您是否必须自己致电Set() 或自行完成。

【讨论】:

    【解决方案5】:

    如果在“一个线程必须停止并等待”达到其“某个点”之前发生“N - 1 个线程中的一个已完成某项任务”,这是否会发生,或者这是否重要?这可能会影响您对同步的选择。

    Rgds, 马丁

    【讨论】:

    • 好吧,工作线程(N - 1 个)总是工作,它们不会阻塞。只是需要停止的1个线程(停止的那个),是必须的,它不能选择是否停止,它必须停止,然后,通过超时或信号,它可以继续进行。 Thanlyyou 问:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    • 2011-07-28
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多