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


当前回答

我会用一些例子来解释:

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

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

其他回答

我会用一些例子来解释:

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

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

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

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.

关于这些词有很多误解。

这是之前的一篇文章(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

锁,互斥,信号量

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

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

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

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

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

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