【发布时间】:2010-09-23 00:05:24
【问题描述】:
谁能解释一下两者的区别:
- 锁定(某个对象){}
- 使用互斥锁
- 使用信号量
- 使用监视器
- 使用其他 .Net 同步类
我就是想不通。在我看来,前两个是一样的?
【问题讨论】:
-
这个链接对我帮助很大:albahari.com/threading
标签: c# multithreading synchronization locking mutex
谁能解释一下两者的区别:
我就是想不通。在我看来,前两个是一样的?
【问题讨论】:
标签: c# multithreading synchronization locking mutex
在大多数情况下,您不应使用锁(=监视器)或互斥体/信号量。它们都在同步操作时阻塞等待线程。所以它们只适用于非常小的操作。
而且你绝对不应该使用 System.Collections.Concurrent 类 - 它们不支持具有多个集合的事务,并且还使用阻塞同步。
令人惊讶的是 .NET 没有有效的非阻塞同步机制。
我在 C# 上实现了来自 GCD (Objc/Swiftworld) 的 serial queue - 非常轻量级,不阻塞使用线程池的同步工具,带有测试。
在大多数情况下,这是同步任何东西的最佳方式 - 从数据库访问 (hello sqlite) 到业务逻辑。
【讨论】:
我在 DotGNU 中为线程提供了类和 CLR 支持,我有一些想法...
除非您需要跨进程锁,否则应始终避免使用互斥锁和信号量。 .NET 中的这些类是 Win32 Mutex 和 Semaphores 的包装器,并且重量相当大(它们需要将上下文切换到内核中,这很昂贵 - 特别是如果您的锁没有处于争用状态)。
正如其他人提到的,C# lock 语句是 Monitor.Enter 和 Monitor.Exit 的编译器魔法(存在于 try/finally 中)。
Monitor 有一个简单但强大的信号/等待机制,而 Mutexes 通过 Monitor.Pulse/Monitor.Wait 方法没有这种机制。 Win32 等价物将是通过 CreateEvent 的事件对象,它实际上也作为 WaitHandles 存在于 .NET 中。 Pulse/Wait 模型类似于 Unix 的 pthread_signal 和 pthread_wait,但速度更快,因为它们在非竞争情况下可以完全是用户模式操作。
Monitor.Pulse/Wait 易于使用。在一个线程中,我们锁定一个对象,检查一个标志/状态/属性,如果它不是我们所期望的,调用 Monitor.Wait 它将释放锁定并等待直到发送一个脉冲。当等待返回时,我们循环返回并再次检查标志/状态/属性。在另一个线程中,每当我们更改标志/状态/属性时,我们都会锁定对象,然后调用 PulseAll 来唤醒所有正在侦听的线程。
通常我们希望我们的类是线程安全的,所以我们在代码中加了锁。但是,通常情况下,我们的类只会被一个线程使用。这意味着锁不必要地减慢了我们的代码……这就是 CLR 中的巧妙优化可以帮助提高性能的地方。
我不确定 Microsoft 的锁实现,但在 DotGNU 和 Mono 中,锁状态标志存储在每个对象的标头中。 .NET(和 Java)中的每个对象都可以成为锁,因此每个对象都需要在其标头中支持这一点。在 DotGNU 实现中,有一个标志允许您为每个用作锁的对象使用全局哈希表——这有利于消除每个对象的 4 字节开销。这对内存来说不是很好(尤其是对于线程不重的嵌入式系统),但会影响性能。
Mono 和 DotGNU 都有效地使用互斥锁来执行锁定/等待,但使用自旋锁样式 compare-and-exchange 操作来消除实际执行硬锁的需要,除非确实有必要:
您可以在此处查看如何实施监视器的示例:
http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup
【讨论】:
如果可以的话,我会尽量避免使用“lock()”、“Mutex”和“Monitor”......
查看 .NET 4 中的新命名空间 System.Collections.Concurrent
它有一些不错的线程安全集合类
http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx
并发字典太棒了!我不再需要手动锁定!
【讨论】:
锁定您使用字符串 ID 标识的任何共享互斥锁的另一个警告是,它将默认为“本地\”互斥锁,并且不会在终端服务器环境中的会话之间共享。
在您的字符串标识符前加上“Global\”,以确保对共享系统资源的访问得到适当控制。在我意识到这一点之前,我刚刚遇到了一大堆与在 SYSTEM 帐户下运行的服务同步通信的问题。
【讨论】:
正如 ECMA 中所述,您可以从 Reflected 方法中观察到 lock 语句基本上等同于
object obj = x;
System.Threading.Monitor.Enter(obj);
try {
…
}
finally {
System.Threading.Monitor.Exit(obj);
}
从上述示例中,我们看到监视器可以锁定对象。
互斥锁在您需要进程间同步时很有用,因为它们可以锁定字符串标识符。不同的进程可以使用相同的字符串标识符来获取锁。
信号量就像类固醇上的互斥锁,它们通过提供最大并发访问计数来允许并发访问。一旦达到限制,信号量就会开始阻止对资源的任何进一步访问,直到其中一个调用者释放信号量。
【讨论】:
关于“使用其他 .Net 同步类”- 您应该了解的其他一些内容:
CCR/TPL(Parallel Extensions CTP)中还有更多(低开销)锁定结构 - 但 IIRC,这些将在 .NET 4.0 中提供
【讨论】:
很好的问题。我可能错了..让我试试..我的原始答案的修订版#2..有了更多的理解。感谢您让我阅读:)
锁(obj)
监视器
使用锁或监视器对于防止同时执行对线程敏感的代码块很有用,但这些构造不允许一个线程将事件传递给另一个线程。这需要同步事件,它们是具有两种状态之一的对象,有信号和无信号,可用于激活和挂起线程。 互斥量、信号量是操作系统级别的概念。例如,使用命名互斥锁,您可以跨多个(托管)exe 进行同步(确保您的应用程序只有一个实例在机器上运行。)
互斥:
Semaphores(伤脑筋)。
【讨论】:
Monitor不允许通信不正确;您仍然可以使用Monitor 等Pulse