有人可以解释以下之间的区别:
- 锁定(某个对象){}
- 使用互斥锁
- 使用信号量
- 使用监视器
- 使用其他 .Net 同步类
我就是想不通。在我看来,前两个是一样的?
有人可以解释以下之间的区别:
我就是想不通。在我看来,前两个是一样的?
好问题。我可能错了..让我试试..我的原始答案的修订版#2..有了更多的理解。谢谢你让我阅读:)
锁(对象)
监视器
使用锁或监视器对于防止同时执行对线程敏感的代码块很有用,但这些构造不允许一个线程将事件传递给另一个线程。这需要同步事件,它们是具有两种状态之一的对象,有信号和无信号,可用于激活和挂起线程。互斥量、信号量是操作系统级别的概念。例如,使用命名互斥锁,您可以跨多个(托管)exe 进行同步(确保您的应用程序只有一个实例在机器上运行。)
互斥体:
信号量(伤害我的大脑)。
重新“使用其他 .Net 同步类”-您应该了解的其他一些:
CCR/TPL(并行扩展CTP)中还有更多(低开销)锁定结构- 但 IIRC,这些将在 .NET 4.0 中提供
正如 ECMA 中所述,您可以从 Reflected 方法中观察到 lock 语句基本上等同于
object obj = x;
System.Threading.Monitor.Enter(obj);
try {
…
}
finally {
System.Threading.Monitor.Exit(obj);
}
从上述示例中,我们看到监视器可以锁定对象。
当您需要进程间同步时,互斥锁很有用,因为它们可以锁定字符串标识符。不同的进程可以使用相同的字符串标识符来获取锁。
信号量就像类固醇上的互斥锁,它们通过提供最大并发访问计数来允许并发访问。一旦达到限制,信号量就会开始阻止对资源的任何进一步访问,直到其中一个调用者释放信号量。
我为 DotGNU 中的线程提供了类和 CLR 支持,我有一些想法......
除非您需要跨进程锁,否则应始终避免使用互斥锁和信号量。.NET 中的这些类是围绕 Win32 Mutex 和 Semaphores 的包装器,并且重量相当大(它们需要将上下文切换到内核中,这很昂贵 - 特别是如果您的锁没有处于争用状态)。
正如其他人所提到的,C# lock 语句是 Monitor.Enter 和 Monitor.Exit 的编译器魔法(存在于 try/finally 中)。
监视器有一个简单但强大的信号/等待机制,而 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 都有效地使用互斥锁来执行锁定/等待,但使用自旋锁风格的比较和交换操作来消除实际执行硬锁的需要,除非真的有必要:
您可以在此处查看如何实现监视器的示例:
http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup
锁定您使用字符串 ID 标识的任何共享互斥锁的另一个警告是,它将默认为“本地\”互斥锁,并且不会在终端服务器环境中的会话之间共享。
使用“Global\”为您的字符串标识符添加前缀,以确保正确控制对共享系统资源的访问。在我意识到这一点之前,我刚刚遇到了一大堆与在 SYSTEM 帐户下运行的服务同步通信的问题。
如果可以的话,我会尽量避免使用“lock()”、“Mutex”和“Monitor”...
查看 .NET 4 中的新命名空间 System.Collections.Concurrent
它有一些不错的线程安全集合类
http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx
并发字典摇滚!我不再需要手动锁定!
在大多数情况下,您不应该使用锁(=Monitors)或互斥体/信号量。它们都在同步操作时阻塞等待线程。所以它们只适用于非常小的操作。
而且你绝对不应该使用 System.Collections.Concurrent
类——它们不支持具有多个集合的事务,并且还使用阻塞同步。
令人惊讶的是,.NET 没有有效的非阻塞同步机制。
我在 C# 上实现了来自 GCD(世界)的串行队列Objc/Swift
- 非常轻量级,不阻塞使用线程池的同步工具,并带有测试。
在大多数情况下,这是同步任何东西的最佳方式——从数据库访问(hello sqlite)到业务逻辑。