我听说过这些与并发编程有关的词,但是锁、互斥量和信号量之间有什么区别呢?
当前回答
大多数问题都可以用(i)锁,(ii)信号量来解决……,或(iii)两者的组合!正如您可能已经发现的,它们非常相似:都防止竞争条件,都有acquire()/release()操作,都导致零或多个线程被阻塞/怀疑…… 实际上,关键的区别仅仅在于它们如何锁定和解锁。
A lock (or mutex) has two states (0 or 1). It can be either unlocked or locked. They're often used to ensure only one thread enters a critical section at a time. A semaphore has many states (0, 1, 2, ...). It can be locked (state 0) or unlocked (states 1, 2, 3, ...). One or more semaphores are often used together to ensure that only one thread enters a critical section precisely when the number of units of some resource has/hasn't reached a particular value (either via counting down to that value or counting up to that value).
对于这两个锁/信号量,在原语处于状态0时试图调用acquire()会导致调用线程挂起。对于锁——获取状态为1的锁的尝试是成功的。对于信号量——尝试获取状态{1,2,3,…是成功的。
对于状态为0的锁,如果之前调用acquire()的同一个线程现在调用release,则释放成功。如果一个不同的线程尝试了这个操作,那么将由实现/库决定发生什么(通常是忽略尝试或抛出错误)。对于处于状态0的信号量,任何线程都可以调用release,并且它将成功(不管之前哪个线程使用acquire将信号量置于状态0)。
从前面的讨论中,我们可以看到锁有一个所有者的概念(唯一可以调用释放的线程是所有者),而信号量没有所有者(任何线程都可以在信号量上调用释放)。
造成很多困惑的是,在实践中,它们是这个高级定义的许多变体。
需要考虑的重要变化:
acquire()/release()应该被称为什么?—[变化很大] 您的锁/信号量是否使用“队列”或“集”来记住等待的线程? 你的锁/信号量可以与其他进程的线程共享吗? 你的锁是“可重入的”吗?——[通常是的]。 你的锁是“阻塞/非阻塞”吗?-[通常非阻塞锁被用作阻塞锁(又名自旋锁),导致繁忙等待]。 如何确保操作是“原子的”?
这取决于你的书/讲师/语言/图书馆/环境。 下面是一些语言是如何回答这些细节的。
C, C+ (pthreads)
A mutex is implemented via pthread_mutex_t. By default, they can't be shared with any other processes (PTHREAD_PROCESS_PRIVATE), however mutex's have an attribute called pshared. When set, so the mutex is shared between processes (PTHREAD_PROCESS_SHARED). A lock is the same thing as a mutex. A semaphore is implemented via sem_t. Similar to mutexes, semaphores can be shared between threasds of many processes or kept private to the threads of one single process. This depends on the pshared argument provided to sem_init.
python (threading.py)
A lock (threading.RLock) is mostly the same as C/C++ pthread_mutex_ts. Both are both reentrant. This means they may only be unlocked by the same thread that locked it. It is the case that sem_t semaphores, threading.Semaphore semaphores and theading.Lock locks are not reentrant -- for it is the case any thread can perform unlock the lock / down the semaphore. A mutex is the same as a lock (the term is not used often in python). A semaphore (threading.Semaphore) is mostly the same as sem_t. Although with sem_t, a queue of thread ids is used to remember the order in which threads became blocked when attempting to lock it while it is locked. When a thread unlocks a semaphore, the first thread in the queue (if there is one) is chosen to be the new owner. The thread identifier is taken off the queue and the semaphore becomes locked again. However, with threading.Semaphore, a set is used instead of a queue, so the order in which threads became blocked is not stored -- any thread in the set may be chosen to be the next owner.
Java (Java . util . concurrent)
A lock (java.util.concurrent.ReentrantLock) is mostly the same as C/C++ pthread_mutex_t's, and Python's threading.RLock in that it also implements a reentrant lock. Sharing locks between processes is harder in Java because of the JVM acting as an intermediary. If a thread tries to unlock a lock it doesn't own, an IllegalMonitorStateException is thrown. A mutex is the same as a lock (the term is not used often in Java). A semaphore (java.util.concurrent.Semaphore) is mostly the same as sem_t and threading.Semaphore. The constructor for Java semaphores accept a fairness boolean parameter that control whether to use a set (false) or a queue (true) for storing the waiting threads.
理论上,信号量经常被讨论,但在实践中,信号量的使用并不多。一个信号量只保存一个整数的状态,所以它通常是相当不灵活的,并且需要同时使用许多个,这导致理解代码的困难。此外,任何线程都可以释放信号量的事实有时是不希望看到的。取而代之的是更多面向对象/高级同步原语/抽象,如“条件变量”和“监视器”。
其他回答
看一看John Kopplin的多线程教程。
在线程间同步一节中,他解释了事件、锁、互斥量、信号量和可等待计时器之间的区别
A mutex can be owned by only one thread at a time, enabling threads to coordinate mutually exclusive access to a shared resource Critical section objects provide synchronization similar to that provided by mutex objects, except that critical section objects can be used only by the threads of a single process Another difference between a mutex and a critical section is that if the critical section object is currently owned by another thread, EnterCriticalSection() waits indefinitely for ownership whereas WaitForSingleObject(), which is used with a mutex, allows you to specify a timeout A semaphore maintains a count between zero and some maximum value, limiting the number of threads that are simultaneously accessing a shared resource.
使用Linux变体上的C编程作为示例的基本情况。
锁:
•通常是一个非常简单的构造二进制在操作中锁定或解锁
•没有线程所有权、优先级、顺序等概念。
•通常是旋转锁,线程不断检查锁的可用性。
•通常依赖于原子操作,例如Test-and-set, compare-and-swap, fetch-and-add等。
•通常需要硬件支持原子操作。
文件锁:
•通常用于协调多个进程对文件的访问。
多个进程可以持有读锁,但是当任何一个进程持有写锁时,不允许其他进程获得读或写锁。
•示例:flock, fcntl等。
互斥:
互斥锁函数调用通常在内核空间中工作,并导致系统调用。
•它使用了所有权的概念。只有当前持有互斥锁的线程才能解锁它。
互斥不是递归的(异常:PTHREAD_MUTEX_RECURSIVE)。
•通常用于与条件变量关联,并作为参数传递给例如pthread_cond_signal, pthread_cond_wait等。
•一些UNIX系统允许多个进程使用互斥锁,尽管这可能不是在所有系统上强制执行。
信号量:
•这是一个内核维护的整数,其值不允许低于零。
•可用于同步进程。
信号量的值可以设置为大于1的值,在这种情况下,该值通常表示可用资源的数量。
•值限制为1和0的信号量称为二进制信号量。
维基百科有一个关于信号量和互斥量区别的很好的章节:
A mutex is essentially the same thing as a binary semaphore and sometimes uses the same basic implementation. The differences between them are: Mutexes have a concept of an owner, which is the process that locked the mutex. Only the process that locked the mutex can unlock it. In contrast, a semaphore has no concept of an owner. Any process can unlock a semaphore. Unlike semaphores, mutexes provide priority inversion safety. Since the mutex knows its current owner, it is possible to promote the priority of the owner whenever a higher-priority task starts waiting on the mutex. Mutexes also provide deletion safety, where the process holding the mutex cannot be accidentally deleted. Semaphores do not provide this.
我的理解是互斥量只能在单个进程中使用,但可以跨多个线程使用,而信号量可以跨多个进程和它们对应的线程集使用。
此外,互斥是二进制的(它要么被锁定要么被解锁),而信号量有计数的概念,或者一个包含多个锁定和解锁请求的队列。
有人能证实我的解释吗?我说的是Linux环境,特别是使用内核2.6.32的Red Hat Enterprise Linux (RHEL)版本6。
关于这些词有很多误解。
这是之前的一篇文章(https://stackoverflow.com/a/24582076/3163691),非常适合这里:
1)临界区(Critical Section) =用户对象,用于允许在一个进程中执行多个活动线程中的一个活动线程。其他未被选中的线程(@获取该对象)将进入睡眠状态。
[没有进程间能力,非常基本的对象]。
2)互斥信号量(Mutex Semaphore,又名Mutex)=内核对象,用于允许在不同的进程中,只执行一个活动线程。其他未被选中的线程(@获取该对象)将进入睡眠状态。该对象支持线程所有权、线程终止通知、递归(同一个线程的多个“acquire”调用)和“优先级反转避免”。
[进程间能力,使用非常安全,一种'高级'同步对象]。
3)计数信号量(又名Semaphore)=内核对象,用于允许来自许多其他线程的一组活动线程的执行。其他未被选中的线程(@获取该对象)将进入睡眠状态。
[然而,进程间功能使用起来不太安全,因为它缺乏以下'互斥'属性:线程终止通知,递归?,“优先倒置规避”?等)。
4)现在,说到“自旋锁”,首先是一些定义:
关键区域(Critical Region):由两个或多个进程共享的内存区域。
Lock=其值允许或拒绝进入“关键区域”的变量。(它可以被实现为一个简单的“布尔标志”)。
忙碌等待=持续测试一个变量,直到某个值出现。
最后:
自旋锁(Spinlock)=一种使用忙等待的锁。(锁的获取是通过xchg或类似的原子操作完成的)。
没有线程睡眠,主要只在内核级使用。对于用户级别代码无效]。
作为最后的评论,我不确定,但我可以跟你打赌,上面的前3个同步对象(#1,#2和#3)使用这个简单的野兽(#4)作为它们实现的一部分。
祝你有愉快的一天!
引用:
-《嵌入式系统的实时概念》,作者:Qing Li和Caroline Yao (CMP Books)。
——安德鲁·塔南鲍姆(皮尔逊教育国际)的《现代操作系统》(第三期)。
-为微软Windows编程应用程序(第4)由Jeffrey Richter(微软编程系列)。
另外,你可以看看看看: https://stackoverflow.com/a/24586803/3163691
推荐文章
- Thread start()和Runnable run()有什么区别
- 在Swift中,什么相当于Objective-C的“@synchronized”?
- 是AsyncTask真的概念上有缺陷或我只是错过了一些东西?
- 信号量和监视器——有什么不同?
- AtomicInteger的实际用途
- 什么是协程?
- Std::lock_guard或Std::scoped_lock?
- Java中的Volatile vs Static
- 当Node.js内部仍然依赖线程时,它是如何固有地更快的?
- Volatile boolean vs AtomicBoolean
- 什么时候我需要在Java中使用AtomicBoolean ?
- ruby有真正的多线程吗?
- 为什么wait()必须总是在同步块中
- 并发、并行和异步方法之间的区别是什么?
- “Java并发实践”仍然有效吗?