我听说过这些与并发编程有关的词,但是锁、互斥量和信号量之间有什么区别呢?


当前回答

维基百科有一个关于信号量和互斥量区别的很好的章节:

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.

其他回答

锁只允许一个线程进入被锁的部分,并且锁不与任何其他进程共享。

互斥锁与锁相同,但它可以是系统范围的(由多个进程共享)。

信号量的作用与互斥量相同,但允许x个线程进入,这可以用于限制同时运行的cpu、io或ram密集型任务的数量。

关于互斥量和信号量区别的更详细的文章请阅读这里。

您还可以使用读/写锁,在任何给定时间允许无限数量的读取器或1个写入器。

这些描述是从。net的角度出发的,对于所有操作系统/语言可能不是100%准确。

我的理解是互斥量只能在单个进程中使用,但可以跨多个线程使用,而信号量可以跨多个进程和它们对应的线程集使用。

此外,互斥是二进制的(它要么被锁定要么被解锁),而信号量有计数的概念,或者一个包含多个锁定和解锁请求的队列。

有人能证实我的解释吗?我说的是Linux环境,特别是使用内核2.6.32的Red Hat Enterprise Linux (RHEL)版本6。

使用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的信号量称为二进制信号量。

我会用一些例子来解释:

Lock:使用Lock的一个例子是将项目(必须有唯一键)添加到共享字典中。 锁将确保当另一个线程(处于临界区)已经通过检查并正在添加项时,一个线程不会进入检查字典中是否存在项的代码机制。如果另一个线程试图输入一个锁定的代码,它将等待(被阻塞),直到对象被释放。

private static readonly Object obj = new Object();

lock (obj) //after object is locked no thread can come in and insert item into dictionary on a different thread right before other thread passed the check...
{
    if (!sharedDict.ContainsKey(key))
    {
        sharedDict.Add(item);
    }
}

信号量: 假设您有一个连接池,那么单个线程可以通过等待信号量获得连接来在池中保留一个元素。然后它使用连接,工作完成后通过释放信号量来释放连接。

我喜欢的代码示例是@Patric给出的一个bouncer -在这里:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace TheNightclub
{
    public class Program
    {
        public static Semaphore Bouncer { get; set; }

        public static void Main(string[] args)
        {
            // Create the semaphore with 3 slots, where 3 are available.
            Bouncer = new Semaphore(3, 3);

            // Open the nightclub.
            OpenNightclub();
        }

        public static void OpenNightclub()
        {
            for (int i = 1; i <= 50; i++)
            {
                // Let each guest enter on an own thread.
                Thread thread = new Thread(new ParameterizedThreadStart(Guest));
                thread.Start(i);
            }
        }

        public static void Guest(object args)
        {
            // Wait to enter the nightclub (a semaphore to be released).
            Console.WriteLine("Guest {0} is waiting to entering nightclub.", args);
            Bouncer.WaitOne();          

            // Do some dancing.
            Console.WriteLine("Guest {0} is doing some dancing.", args);
            Thread.Sleep(500);

            // Let one guest out (release one semaphore).
            Console.WriteLine("Guest {0} is leaving the nightclub.", args);
            Bouncer.Release(1);
        }
    }
}

互斥量基本上就是信号量(1,1),并且经常在全局范围内使用(应用范围内使用,否则可以说锁更合适)。当从全局可访问的列表中删除节点时,可以使用全局互斥(在删除节点时,最不希望另一个线程做一些事情)。当你获得互斥锁时,如果不同的线程试图获得同一个互斥锁,它将被置于睡眠状态,直到获得互斥锁的同一线程释放它。

@deepe是创建全局互斥的一个很好的例子

class SingleGlobalInstance : IDisposable
{
    public bool hasHandle = false;
    Mutex mutex;

    private void InitMutex()
    {
        string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
        string mutexId = string.Format("Global\\{{{0}}}", appGuid);
        mutex = new Mutex(false, mutexId);

        var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
        var securitySettings = new MutexSecurity();
        securitySettings.AddAccessRule(allowEveryoneRule);
        mutex.SetAccessControl(securitySettings);
    }

    public SingleGlobalInstance(int timeOut)
    {
        InitMutex();
        try
        {
            if(timeOut < 0)
                hasHandle = mutex.WaitOne(Timeout.Infinite, false);
            else
                hasHandle = mutex.WaitOne(timeOut, false);

            if (hasHandle == false)
                throw new TimeoutException("Timeout waiting for exclusive access on SingleInstance");
        }
        catch (AbandonedMutexException)
        {
            hasHandle = true;
        }
    }


    public void Dispose()
    {
        if (mutex != null)
        {
            if (hasHandle)
                mutex.ReleaseMutex();
            mutex.Dispose();
        }
    }
}

然后用like:

using (new SingleGlobalInstance(1000)) //1000ms timeout on global lock
{
    //Only 1 of these runs at a time
    GlobalNodeList.Remove(node)
}

希望这能为您节省一些时间。

看一看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.