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


当前回答

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

其他回答

锁,互斥,信号量

这是一个普遍的愿景。细节取决于真正的语言实现

锁线程同步工具。当线程获得锁时,它就变成了一个能够执行代码块的单一线程。所有其他线程都被阻塞。只有拥有锁的线程才能解锁

互斥锁——互斥锁。这是一种锁。在某些语言中它是进程间机制,在某些语言中它是锁的同义词。例如,Java在synchronized和Java .util.concurrent.locks. lock中使用锁

信号量——允许多个线程访问共享资源。你可以发现互斥也可以通过信号量来实现。它是一个独立的对象,用于管理对共享资源的访问。您可以发现任何线程都可以发出信号并解除阻塞。它也被用于信号

[iOS锁,互斥量,信号量]

支持所有权、最大进程共享锁数和临界区允许的最大进程/线程数是决定具有通用锁名的并发对象的名称/类型的三个主要因素。由于这些因素的值是二进制的(有两种状态),我们可以将它们总结为一个3*8的类真值表。

X(支持所有权?):no(0) / yes(1) Y(#共享进程):> 1(∞)/ 1 Z (#processes/threads in CA): > 1(∞)/ 1


  X   Y   Z          Name
 --- --- --- ------------------------
  0   ∞   ∞   Semaphore              
  0   ∞   1   Binary Semaphore       
  0   1   ∞   SemaphoreSlim          
  0   1   1   Binary SemaphoreSlim(?)
  1   ∞   ∞   Recursive-Mutex(?)     
  1   ∞   1   Mutex                  
  1   1   ∞   N/A(?)                 
  1   1   1   Lock/Monitor           

请随意编辑或展开这个表,我已经把它作为一个ascii表进行编辑:)

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

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

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

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

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

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

我会用一些例子来解释:

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)
}

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

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