什么是信号量?

16 浏览
0 Comments

什么是信号量?

信号量是一个经常用于解决多线程问题的编程概念。我的问题是:

信号量是什么,以及如何使用它?

admin 更改状态以发布 2023年5月21日
0
0 Comments

迈克尔·巴尔的文章《互斥锁和信号量的启示录》是对互斥锁和信号量有何不同,以及何时应该使用它们和不应该使用它们的简短介绍。我在这里摘录了几个关键段落。

关键在于互斥锁应该用于保护共享资源,而信号量应该用于信令。通常不应该使用信号量来保护共享资源,也不应该使用互斥锁进行信令。例如,在使用信号量来保护共享资源时,使用类似于保安的比喻可能存在问题,您可以使用这种方式,但可能会导致难以诊断的错误。

虽然互斥锁和信号量在实现上有相似之处,但它们应该始终以不同的方式使用。

对于在顶部提出的问题的最常见(但仍不正确的)答案是互斥锁和信号量非常相似,唯一的显著差异是信号量可以计数高于1。几乎所有工程师似乎都正确地理解互斥锁是用于通过确保代码的关键部分内的互斥排除来保护共享资源的二进制标志。但是,当被问及如何使用“计数信号量”时,大多数工程师-仅在信心程度方面有所不同-表达了一些教科书般的观点,即这些被用于保护若干等价资源。

...

在这一点上,使用浴室钥匙来保护共享资源-浴室的想法进行了有趣的比喻。如果商店只有一个浴室,则单个钥匙就足以保护该资源并防止多人同时使用。

如果有多个浴室,你可能会想把它们钥匙设成一样的,然后制造多把钥匙,这与信号量被错误使用类似。一旦你拥有了一把钥匙,你实际上并不知道哪个浴室是可用的,如果你走这条路,你很可能最终要使用互斥锁来提供信息,确保你不会使用已经被占用的浴室。

信号量是保护多个基本相同资源的错误工具,但这是很多人的想法和用法。保镖的比喻具有明显的不同之处——没有几个相同类型的资源,而是一个可以接受多个同时用户的资源。我想信号量可以在这种情况下使用,但是真正存在的情况很少可以用这个比喻来解释——更常见的情况是有几个相同类型的资源,但仍然是独立的资源,比如浴室,不能这样使用。

……

信号量的正确使用是用于从一个任务向另一个任务发送信号。互斥锁的意思是每个使用它来保护所共享资源的任务都要取和释放锁(always in that order)。相比之下,使用信号量的任务,要么发送信号,要么等待(不会同时进行)。例如,任务1可以包含代码,以便在按下“电源”按钮时发布(即发送或增加)特定的信号量,而任务2则唤醒显示屏并在同一信号量上等待。在这种情况下,一个任务是事件信号的生产者,另一个任务是消费者。

……

在这里,有个重要的观点是互斥锁以一种不好的方式干扰实时操作系统,导致优先级反转,使得一个次要任务可能在更重要的任务之前执行,因为它们共享资源。简而言之,当一个较低优先级的任务使用互斥锁获取资源A,然后尝试获取B时,因为B不可用而被暂停。等待期间,一个优先级更高的任务需要A,但它已经被占用,并且是由一个甚至没有运行的进程占用,因为它在等待B。虽然有许多方法可以解决这个问题,但最常见的方法是修改互斥锁和任务管理器。在这些情况下,互斥锁比二元信号量复杂得多,如果在这种情况下使用信号量,将导致优先级反转,因为任务管理器不知道优先级反转,也无法采取措施来纠正它。

...

导致现代混淆Mutex和Semaphore之间的普遍性的原因是历史的,因为它可以追溯到Djikstra于1974年发明信号量(在本文中为大写字母“ S”)之前。在那个日期之前,计算机科学家所知道的中断安全任务同步和信号机制中没有一种机制能够有效地扩展使用到超过两个任务。Dijkstra的革命性、安全和可扩展的信号量被应用于关键部分保护和信号。于是混淆就开始了。

然而,随着基于优先级的抢占式RTOS(如VRTX,约1980年)、关于RMA以及优先级倒置所引起的问题的学术论文的出现,以及1990年关于优先级继承协议的论文,操作系统开发人员后来发现,互斥锁必须不仅仅是带有二进制计数器的信号量。

互斥锁:资源共享

信号量:信号通知

不要在仔细考虑副作用之前将一个用于另一个。

0
0 Comments

可以把信号量看作夜店门卫。夜店里面同时只能进入限定的人数,如果夜店已满则不允许有人进入,但是只要有人离开又有可能有新人进入。

信号量就是限制特定资源使用者数量的一种方法。例如,在应用程序中限制对数据库的同时调用数量。

这里是一个使用C#的教学示例:

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

0