5天不再惧怕多线程——第四天 信号量

简介:

    今天整理“信号量”的相关知识,其实想想也蛮有趣的,锁,互斥,信号量都可以实现线程同步,在framework里面主要有三种。

<1>:ManualResetEvent

<2>:AutoResetEvent

<3>: Semaphore

 

好,下面就具体看看这些玩意的使用。

 

一:ManualResetEvent

      该对象有两种信号量状态True和False,好奇的我们肯定想知道True和False有什么区别,稍后的例子见分晓,有三个方法值得学习一下。

1:WaitOne

     该方法用于阻塞线程,默认是无限期的阻塞,有时我们并不想这样,而是采取超时阻塞的方法,如果超时就放弃阻塞,这样也就避免了无限期

       等待的尴尬。

2:Set

     手动修改信号量为True,也就是恢复线程执行。

3:ReSet

     手动修改信号量为False,暂停线程执行。

 

好了,下面举个例子说明一下。

 

<1>  信号量初始为False,WaitOne采用无限期阻塞,可以发现线程间可以进行交互。

public class Example
{
    public static void Main()
    {
        Thread t = new Thread(Run);

        t.Name = "Jack";

        Console.WriteLine("当前时间:{0}  {1} {1},我是主线程,收到请回答。", DateTime.Now, t.Name);

        t.Start();

        Thread.Sleep(5000);

        mr.Set();

        Console.Read();
    }

    static ManualResetEvent mr = new ManualResetEvent(false);

    static void Run()
    {
        mr.WaitOne();

        Console.WriteLine("\n当前时间:{0}  主线程,主线程,{1}已收到!", DateTime.Now, Thread.CurrentThread.Name);
    }
}

 

<2> 信号量初始为True,WaitOne采用无限期阻塞,实验发现WaitOne其实并没有被阻塞。

 static ManualResetEvent mr = new ManualResetEvent(true);

 

<3>信号量初始为False,WaitOne采用超时2s,虽然主线程要等5s才能进行Set操作,但是WaitOne已经等不及提前执行了。

public class Example
{
    public static void Main()
    {
        Thread t = new Thread(Run);

        t.Name = "Jack";

        Console.WriteLine("当前时间:{0}  {1} {1},我是主线程,收到请回答。", DateTime.Now, t.Name);

        t.Start();

        Thread.Sleep(5000);

        mr.Set();

        Console.Read();
    }

    static ManualResetEvent mr = new ManualResetEvent(false);

    static void Run()
    {
        mr.WaitOne(2000);

        Console.WriteLine("\n当前时间:{0}  主线程,主线程,{1}已收到!", DateTime.Now, Thread.CurrentThread.Name);
    }
}


二:AutoResetEvent

      在VS对象浏览器中,我们发现AutoResetEvent和ManualResetEvent都是继承于EventWaitHandle,所以基本功能是一样的,不过值得注意

的一个区别是WaitOne会改变信号量的值,比如说初始信号量为True,如果WaitOne超时信号量将自动变为False,而ManualResetEvent则不会。

public class Example
{
    public static void Main()
    {
        Thread t = new Thread(Run);

        t.Name = "Jack";

        t.Start();

        Console.Read();
    }

    static AutoResetEvent ar = new AutoResetEvent(true);

    static void Run()
    {
        var state = ar.WaitOne(1000, true);

        Console.WriteLine("我当前的信号量状态:{0}", state);

        state = ar.WaitOne(1000, true);

        Console.WriteLine("我恨你,不理我,您现在的状态是:{0}", state);

    }
}

 

三:Semaphore 

     这玩意是.net 4.0新增的,用于控制线程的访问数量,默认的构造函数为initialCount和maximumCount,表示默认设置的信号量个数和

最大信号量个数,其实说到底,里面是采用计数器来来分配信号量,当你WaitOne的时候,信号量自减,当Release的时候,信号量自增,然而

当信号量为0的时候,后续的线程就不能拿到WaitOne了,所以必须等待先前的线程通过Release来释放。

 

好了,下面还是举例子来说明一下:

 

<1> initialCount=1,maximunCount=10,WaitOne采用无限期等待。


namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {

            Thread t1 = new Thread(Run1);
            t1.Start();

            Thread t2 = new Thread(Run2);
            t2.Start();

            Console.Read();
        }

        static Semaphore sem = new Semaphore(1, 10);

        static void Run1()
        {
            sem.WaitOne();

            Console.WriteLine("大家好,我是Run1");
        }

        static void Run2()
        {
            sem.WaitOne();

            Console.WriteLine("大家好,我是Run2");
        }
    }
}

我们悲剧的发现t2线程不能执行,我们知道WaitOne相当于自减信号量,然而默认的信号量个数为1,所以t2想执行必须等待t1通过Release来释放。

static void Run1()
        {
            sem.WaitOne();

            Console.WriteLine("大家好,我是Run1");

            sem.Release();
        }

 

可能有的同学要问,我不是设置了maximunCount=10吗?为什么没有起到作用?是的,默认情况下是没有起到作用,必须要我们手动干预一下,

我们知道调用Release方法相当于自增一个信号量,然而Release有一个重载,可以指定自增到maximunCount个信号量,这里我就在主线程上

Release(10),看看效果。

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {

            Thread t1 = new Thread(Run1);
            t1.Start();

            Thread t2 = new Thread(Run2);
            t2.Start();

            Thread.Sleep(1000);

            sem.Release(10);

            Console.Read();
        }

        static Semaphore sem = new Semaphore(1, 10);

        static void Run1()
        {
            sem.WaitOne();

            Console.WriteLine("大家好,我是Run1");
        }

        static void Run2()
        {
            sem.WaitOne();

            Console.WriteLine("大家好,我是Run2");
        }
    }
}


<2> Semaphore命名,升级进程交互。

      在VS对象浏览器中发现Semaphore是继承字WaitHandle,而WaitHandle封装了win32的一些同步机制,所以当我们给Semaphore命名的时候

就会在系统中可见,下面举个例子,把下面的代码copy一份,运行两个程序。


namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {

            Thread t1 = new Thread(Run1);
            t1.Start();

            Thread t2 = new Thread(Run2);
            t2.Start();

            Console.Read();
        }

        static Semaphore sem = new Semaphore(3, 10, "cnblogs");

        static void Run1()
        {
            sem.WaitOne();

            Console.WriteLine("当前时间:{0} 大家好,我是Run1", DateTime.Now);
        }

        static void Run2()
        {
            sem.WaitOne();

            Console.WriteLine("当前时间:{0} 大家好,我是Run2", DateTime.Now);
        }
    }
}

 

是的,我设置了信号量是3个,所以只能有三个线程持有WaitOne,后续的线程只能苦苦的等待。



相关文章
|
2月前
|
运维 API 计算机视觉
深度解密协程锁、信号量以及线程锁的实现原理
深度解密协程锁、信号量以及线程锁的实现原理
43 1
|
3月前
|
Java 数据中心 微服务
Java高级知识:线程池隔离与信号量隔离的实战应用
在Java并发编程中,线程池隔离与信号量隔离是两种常用的资源隔离技术,它们在提高系统稳定性、防止系统过载方面发挥着重要作用。
57 0
|
4月前
|
安全 C++
利用信号量实现线程顺序执行
【8月更文挑战第25天】信号量是多线程编程中用于控制共享资源访问的关键同步机制,能有效保证线程按预设顺序执行。实现方法包括:引入相关头文件(如 C++ 中的 `&lt;semaphore.h&gt;`),创建信号量并通过 `sem_init` 设置初始值;在各线程函数中运用 `sem_post` 与 `sem_wait` 来传递执行权;最后,通过 `sem_destroy` 销毁信号量以释放资源。使用过程中需注意错误处理、确保线程安全及合理设定信号量初值,以维持程序稳定性和高效性。
|
4月前
利用信号量实现线程顺序执行
【8月更文挑战第24天】本文介绍了如何运用信号量确保多线程程序中线程按预定顺序执行的方法。信号量作为同步机制,可有效控制共享资源访问,防止数据不一致。实现步骤包括:引入必要的头文件(如 `&lt;pthread.h&gt;` 和 `&lt;semaphore.h&gt;`),定义信号量变量(如 `sem_t` 类型),初始化信号量(通常第一个信号量设为1,其余设为0),以及创建线程(每个线程执行特定任务并释放相应信号量)。
|
4月前
|
数据采集 Java Python
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
|
5月前
|
监控 Cloud Native Java
通用快照方案问题之Hystrix和Ribbon在超时设置上的冲突如何解决
通用快照方案问题之Hystrix和Ribbon在超时设置上的冲突如何解决
54 0
|
6月前
|
API
java-多线程-CountDownLatch(闭锁) CyclicBarrier(栅栏) Semaphore(信号量)-
java-多线程-CountDownLatch(闭锁) CyclicBarrier(栅栏) Semaphore(信号量)-
39 1
|
7月前
|
安全 Java API
多线程(JUC, ReentrantLock, 原子类, 线程池, 信号量 Semaphore, CountDownLatch)
多线程(JUC, ReentrantLock, 原子类, 线程池, 信号量 Semaphore, CountDownLatch)
58 4
|
7月前
|
算法 安全 Unix
【C++ 20 信号量 】C++ 线程同步新特性 C++ 20 std::counting_semaphore 信号量的用法 控制对共享资源的并发访问
【C++ 20 信号量 】C++ 线程同步新特性 C++ 20 std::counting_semaphore 信号量的用法 控制对共享资源的并发访问
193 0
|
7月前
信号量(Semaphore)与线程计数器(CountDownLatch)(详解)
信号量(Semaphore)与线程计数器(CountDownLatch)(详解)
68 0
信号量(Semaphore)与线程计数器(CountDownLatch)(详解)