C# 中各种线程同步选项之间有什么区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/301160/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What are the differences between various threading synchronization options in C#?
提问by user38834
Can someone explain the difference between:
有人可以解释以下之间的区别:
- lock (someobject) {}
- Using Mutex
- Using Semaphore
- Using Monitor
- Using Other .Net synchronization classes
- 锁定(某个对象){}
- 使用互斥锁
- 使用信号量
- 使用监视器
- 使用其他 .Net 同步类
I just can't figure it out. It seems to me the first two are the same?
我就是想不通。在我看来前两个是一样的?
回答by Gishu
Great question. I maybe wrong.. Let me try.. Revision#2 of my orig answer.. with a little bit of more understanding. Thanks for making me read :)
很好的问题。我可能错了..让我试试..我的原答案的修订版#2..多一点理解。谢谢你让我读:)
lock(obj)
锁(对象)
- is a CLR construct that for (intra-object?) thread synchronization. Ensures that only one thread can take ownership of the object's lock & enter the locked block of code. Other threads must wait till the current owner relinquishes the lock by exiting the block of code. Also it is recommended that you lock on a private member object of your class.
- 是用于(对象内?)线程同步的 CLR 构造。确保只有一个线程可以拥有对象的锁并进入锁定的代码块。其他线程必须等到当前所有者通过退出代码块来放弃锁定。此外,建议您锁定类的私有成员对象。
Monitors
监视器
- lock(obj) is implemented internally using a Monitor.You should prefer lock(obj) because it prevents you from goofing up like forgetting the cleanup procedure. It 'idiot-proof's the Monitor construct if you will.
Using Monitor is generally preferred over mutexes, because monitors were designed specifically for the .NET Framework and therefore make better use of resources.
- lock(obj) 是使用 Monitor 在内部实现的。您应该更喜欢 lock(obj) ,因为它可以防止您像忘记清理过程一样愚蠢。如果您愿意,它是“防白痴”的 Monitor 结构。
使用监视器通常比互斥体更受欢迎,因为监视器是专门为 .NET Framework 设计的,因此可以更好地利用资源。
Using a lock or monitor is useful for preventing the simultaneous execution of thread-sensitive blocks of code, but these constructs do not allow one thread to communicate an event to another. This requires synchronization events, which are objects that have one of two states, signaled and un-signaled, that can be used to activate and suspend threads. Mutex, Semaphores are OS-level concepts. e.g with a named mutex you could synchronize across multiple (managed) exes (ensuring that only one instance of your application is running on the machine.)
使用锁或监视器对于防止线程敏感代码块的同时执行很有用,但这些构造不允许一个线程与另一个线程通信。这需要同步事件,它们是具有两种状态之一的对象,有信号和无信号,可用于激活和挂起线程。互斥量、信号量是操作系统级别的概念。例如,使用命名的互斥锁,您可以跨多个(托管)exe 进行同步(确保只有一个应用程序实例在机器上运行。)
Mutex:
互斥体:
- Unlike monitors, however, a mutex can be used to synchronize threads across processes.When used for inter-process synchronization, a mutex is called a named mutexbecause it is to be used in another application, and therefore it cannot be shared by means of a global or static variable. It must be given a name so that both applications can access the same mutex object. In contrast, the Mutex class is a wrapper to a Win32 construct. While it is more powerful than a monitor, a mutex requires interop transitions that are more computationally expensive than those required by the Monitor class.
- 然而,与监视器不同的是,互斥锁可用于跨进程同步线程。当用于进程间同步时,互斥锁被称为命名互斥锁,因为它要在另一个应用程序中使用,因此它不能通过全局或静态变量共享。必须为其命名,以便两个应用程序都可以访问同一个互斥对象。相比之下,Mutex 类是 Win32 构造的包装器。虽然它比监视器更强大,但互斥体需要的互操作转换比 Monitor 类所需的计算成本更高。
Semaphores(hurt my brain).
信号量(伤害我的大脑)。
- Use the Semaphore class to control access to a pool of resources. Threads enter the semaphore by calling the WaitOne method, which is inherited from the WaitHandle class, and release the semaphore by calling the Release method. The count on a semaphore is decremented each time a thread enters the semaphore, and incremented when a thread releases the semaphore. When the count is zero, subsequent requests block until other threads release the semaphore. When all threads have released the semaphore, the count is at the maximum value specified when the semaphore was created. A thread can enter the semaphore multiple times..The Semaphore class does not enforce thread identity on WaitOne or Release.. programmers responsibility to not muck up.Semaphores are of two types: local semaphores and named system semaphores. If you create a Semaphore object using a constructor that accepts a name, it is associated with an operating-system semaphore of that name.Named system semaphores are visible throughout the operating system, and can be used to synchronize the activities of processes. A local semaphore exists only within your process. It can be used by any thread in your process that has a reference to the local Semaphore object. Each Semaphore object is a separate local semaphore.
- 使用 Semaphore 类来控制对资源池的访问。线程通过调用继承自WaitHandle类的WaitOne方法进入信号量,调用Release方法释放信号量。每次线程进入信号量时,信号量的计数递减,当线程释放信号量时递增。当计数为零时,后续请求将阻塞,直到其他线程释放信号量。当所有线程都释放了信号量时,计数处于创建信号量时指定的最大值。 一个线程可以多次进入信号量..信号量类不会在 WaitOne 或 Release 上强制执行线程标识.. 程序员有责任不搞砸。信号量有两种类型:本地信号量和命名信号量系统信号量。如果使用接受名称的构造函数创建信号量对象,则它与该名称的操作系统信号量相关联。命名系统信号量在整个操作系统中都是可见的,可用于同步进程的活动。 本地信号量仅存在于您的进程中。它可以被进程中任何引用本地信号量对象的线程使用。每个信号量对象都是一个单独的本地信号量。
回答by arul
As stated in ECMA, and as you can observe from Reflected methods the lock statement is basically equivalent to
正如 ECMA 中所述,并且您可以从 Reflected 方法中观察到,lock 语句基本上等同于
object obj = x;
System.Threading.Monitor.Enter(obj);
try {
…
}
finally {
System.Threading.Monitor.Exit(obj);
}
From the aforementioned example we see that Monitors can lock on objects.
从上面的例子我们看到监视器可以锁定对象。
Mutexe's are useful when you need interprocess synchronization as they canlock on a string identifier. The same string identifier can be used by different processes to acquire the lock.
当您需要进程间同步时,互斥体很有用,因为它们可以锁定字符串标识符。不同的进程可以使用相同的字符串标识符来获取锁。
Semaphores are like Mutexes on steroids, they allow concurrent access by providing a maximum count of concurrent access'. Once the limit is reached the semaphore starts blocking any further access to the resource until one of the callers releases the semaphore.
信号量就像类固醇上的互斥体,它们通过提供最大并发访问计数来允许并发访问。一旦达到限制,信号量开始阻止对资源的任何进一步访问,直到调用者之一释放信号量。
回答by Marc Gravell
Re "Using Other .Net synchronization classes"- some of the others you should know about:
重新“使用其他 .Net 同步类”-您应该了解的其他一些:
- ReaderWriterLock- allows multiple readers or a single writer (not at the same time)
- ReaderWriterLockSlim- like above, lower overhead
- ManualResetEvent- a gate that allows code past when open
- AutoResetEvent- as above, but shuts automatically once opened
- ReaderWriterLock- 允许多个读者或单个作者(不是同时)
- ReaderWriterLockSlim- 和上面一样,开销更低
- ManualResetEvent- 打开时允许代码过去的门
- AutoResetEvent- 同上,但打开后自动关闭
There are also more (low overhead) locking constructs in CCR/TPL (the Parallel ExtensionsCTP) - but IIRC, these will be made available in .NET 4.0
CCR/TPL(并行扩展CTP)中还有更多(低开销)锁定结构——但是 IIRC,这些将在 .NET 4.0 中可用
回答by nvuono
An additional caveat for locking on any shared Mutex you've identified with a string ID is that it will default to a "Local\" mutex and will not be shared across sessions in a terminal server environment.
锁定您使用字符串 ID 标识的任何共享互斥锁的另一个警告是,它将默认为“本地\”互斥锁,并且不会在终端服务器环境中的会话之间共享。
Prefix your string identifier with "Global\" to ensure that access to shared system resources is properly controlled. I was just running into a whole heap of problems synchronizing communications with a service running under the SYSTEM account before I realized this.
使用“Global\”作为字符串标识符的前缀,以确保对共享系统资源的访问得到正确控制。在我意识到这一点之前,我只是遇到了与在 SYSTEM 帐户下运行的服务同步通信的一大堆问题。
回答by Peter Gfader
I would try to avoid "lock()", "Mutex" and "Monitor" if you can...
如果可以的话,我会尽量避免“lock()”、“Mutex”和“Monitor”……
Check out the new namespace System.Collections.Concurrent in .NET 4
It has some nice thread-safe collection classes
查看 .NET 4 中的新命名空间 System.Collections.Concurrent
它有一些不错的线程安全集合类
http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx
http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx
ConcurrentDictionary rocks! no manual locking anymore for me!
ConcurrentDictionary 摇滚!我不再需要手动锁定了!
回答by tumtumtum
I did the classes & CLR support for threading in DotGNU and I have a few thoughts...
我在 DotGNU 中为线程提供了类和 CLR 支持,我有一些想法......
Unless you require cross process locks you should always avoid using Mutex & Semaphores. These classes in .NET are wrappers around the Win32 Mutex and Semaphores and are rather heavy weight (they require a context switch into the Kernel which is expensive - especially if your lock is not under contention).
除非您需要跨进程锁,否则您应该始终避免使用互斥锁和信号量。.NET 中的这些类是 Win32 Mutex 和 Semaphore 的包装器,并且重量相当大(它们需要将上下文切换到内核中,这很昂贵 - 特别是在您的锁没有争用的情况下)。
As others are mentioned, the C# lock statement is compiler magic for Monitor.Enter and Monitor.Exit (existing within a try/finally).
正如其他人提到的,C# lock 语句是 Monitor.Enter 和 Monitor.Exit(存在于 try/finally 中)的编译器魔术。
Monitors have a simple but powerful signal/wait mechanism that Mutexes don't have via the Monitor.Pulse/Monitor.Wait methods. The Win32 equivalent would be event objects via CreateEvent which actually also exist in .NET as WaitHandles. The Pulse/Wait model is similar to Unix's pthread_signal and pthread_wait but are faster because they can be entirely user-mode operations in the un-contended case.
监视器具有简单但强大的信号/等待机制,互斥体通过 Monitor.Pulse/Monitor.Wait 方法没有这种机制。Win32 等效项是通过 CreateEvent 的事件对象,它实际上也作为 WaitHandles 存在于 .NET 中。Pulse/Wait 模型类似于 Unix 的 pthread_signal 和 pthread_wait,但速度更快,因为它们在无竞争的情况下可以完全是用户模式操作。
Monitor.Pulse/Wait is simple to use. In one thread, we lock an object, check a flag/state/property and if it's not what we are expecting, call Monitor.Wait which will release the lock and wait until a pulse is sent. When the wait returns, we loop back and check the flag/state/property again. In the other thread, we lock the object whenever we change the flag/state/property and then call PulseAll to wake up any listening threads.
Monitor.Pulse/Wait 使用简单。在一个线程中,我们锁定一个对象,检查一个标志/状态/属性,如果它不是我们所期望的,调用 Monitor.Wait 它将释放锁定并等待直到发送一个脉冲。当等待返回时,我们循环返回并再次检查标志/状态/属性。在另一个线程中,每当我们更改标志/状态/属性时,我们都会锁定对象,然后调用 PulseAll 来唤醒所有监听线程。
Often we want our classes to be thread safe so we put locks in our code. However, it is often the case that our class will only ever be used by one thread. This means the locks needlessly slow down our code...this is where clever optimisations in the CLR can help improve performance.
通常我们希望我们的类是线程安全的,所以我们在代码中加锁。然而,通常情况下我们的类只会被一个线程使用。这意味着锁会不必要地减慢我们的代码……这就是 CLR 中的巧妙优化可以帮助提高性能的地方。
I'm not sure about Microsoft's implementation of locks but in DotGNU and Mono, a lock state flag is stored in the header of every object. Every object in .NET (and Java) can become a lock so every object needs to support this in their header. In the DotGNU implementation, there is a flag that allows you to use a global hashtable for every object that is used as a lock -- this has the benefit of eliminating a 4 byte overhead for every object. This is not great for memory (especially for embedded systems that aren't heavily threaded) but has a hit on performance.
我不确定 Microsoft 对锁的实现,但在 DotGNU 和 Mono 中,锁状态标志存储在每个对象的标头中。.NET(和 Java)中的每个对象都可以成为一个锁,因此每个对象都需要在它们的头文件中支持这一点。在 DotGNU 实现中,有一个标志允许您为每个用作锁的对象使用全局哈希表——这有利于消除每个对象的 4 字节开销。这对内存来说不是很好(特别是对于线程不密集的嵌入式系统),但会影响性能。
Both Mono and DotGNU effectively use mutexes to perform locking/waiting but use a spinlock style compare-and-exchangeoperations to eliminate the need to actually perform a hard locks unless really necessary:
Mono 和 DotGNU 都有效地使用互斥锁来执行锁定/等待,但使用自旋锁样式的比较和交换操作来消除实际执行硬锁的需要,除非确实有必要:
You can see an example of how monitors can be implemented here:
您可以在此处查看如何实施监视器的示例:
http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup
http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup
回答by Alexander Danilov
In most cases you should notuse locks (=Monitors) or mutexes/semaphores. They all block current thread.
在大多数情况下,您不应使用锁(=监视器)或互斥锁/信号量。它们都阻塞当前线程。
And you definitely shouldn't useSystem.Collections.Concurrent
classes - they are the main source of race conditions because don't support transactions between multiple collections, and also block current thread.
而且您绝对不应该使用System.Collections.Concurrent
类 - 它们是竞争条件的主要来源,因为不支持多个集合之间的事务,并且还会阻塞当前线程。
Surprisingly .NET doesn't have effective mechanisms for synchronization.
令人惊讶的是,.NET 没有有效的同步机制。
I implemented serial queuefrom GCD (Objc/Swift
world) on C# - very lightweight, not blocking synchronization tool that uses thread pool, with tests.
我在 C# 上实现了来自 GCD(世界)的串行队列Objc/Swift
- 非常轻量级,不阻塞使用线程池的同步工具,并带有测试。
It is the best way to synchronize anything in most cases - from database access (hello sqlite) to business logic.
在大多数情况下,这是同步任何东西的最佳方式——从数据库访问(hello sqlite)到业务逻辑。