• 改善C#程序的建议6:在线程同步中使用信号量


    所谓线程同步,就是多个线程之间在某个对象上执行等待(也可理解为锁定该对象),直到该对象被解除锁定。C#中对象的类型分为引用类型和值类型。CLR在这两种类型上的等待是不一样的。我们可以简单的理解为在CLR中,值类型是不能被锁定的,也即:不能在一个值类型对象上执行等待。而在引用类型上的等待机制,则分为两类:锁定和信号同步。

    锁定,使用关键字lock和类型Monitor。两者没有实质区别,前者其实是后者的语法糖。这是最常用的同步技术;

    本建议我们讨论的是信号同步。信号同步机制中涉及的类型都继承自抽象类WaitHandle,这些类型有EventWaitHandle(类型化为AutoResetEvent、ManualResetEvent)和Semaphore以及Mutex。见类图6-3:

    clip_image002

    图 同步功能类类图

    EventWaitHandle(子类为AutoResetEvent、ManualResetEvent)和Semaphore以及Mutex都继承自WaitHandle,所以它们底层的原理是一致的,维护的都是一个系统内核句柄。不过我们仍需简单的区分下这三类类型。

    EventWaitHandle,维护一个由内核产生的布尔类型对象(我们称之为“阻滞状态”),如果其值为false,那么在它上面等待的线程就阻塞。可以调用类型的Set方法将其值设置为true,解除阻塞。EventWaitHandle类型的两个子类AutoResetEvent和ManualResetEvent,它们的区别并不大,本建议接下来会针对它们阐述如何正确使用信号量。

    Semaphore,维护一个由内核产生的整型变量,如果其值为0,则在它上面等待的线程就阻塞,其值大于0,就解除阻塞,同时,每解除阻塞一个线程,其值就减1。

    EventWaitHandle和Semaphore提供的都是单应用程序域内的线程同步功能,Mutex则不同,它为我们提供了跨应用程序域阻塞和解除阻塞线程的能力。


    1:使用信号机制提供线程同步的一个简单的例子

    使用信号机制提供线程同步的一个简单的例子如下:

    AutoResetEvent autoResetEvent =new AutoResetEvent(false);

    privatevoid buttonStartAThread_Click(object sender, EventArgs e)
    {
    Thread tWork
    =new Thread(() =>
    {
    label1.Text
    ="线程启动..."+ Environment.NewLine;
    label1.Text
    +="开始处理一些实际的工作"+ Environment.NewLine;
    //省略工作代码
    label1.Text +="我开始等待别的线程给我信号,才愿意继续下去"+ Environment.NewLine;
    autoResetEvent.WaitOne();
    label1.Text
    +="我继续做一些工作,然后结束了!";
    //省略工作代码
    });
    tWork.IsBackground
    =true;
    tWork.Start();
    }

    privatevoid buttonSet_Click(object sender, EventArgs e)
    {
    //给在autoResetEvent上等待的线程一个信号
    autoResetEvent.Set();
    }

    这是一个简单的Winform窗体程序,其中一个按钮负责开启一个新的线程,还有一个按钮负责给刚开启的那个线程发送信号。现在详细解释这里面发生的事情。

    AutoResetEvent autoResetEvent =new AutoResetEvent(false);

    这段代码创建了一个同步类型对象autoResetEvent,它设置自己的默认阻滞状态是false。这意味着任何在它上面进行等待的线程将会被阻滞。所谓进行等待,就是在线程中应用:

    autoResetEvent.WaitOne();

    这说明tWork开始在autoResetEvent上等待任何其它地方给它的信号。信号来了,则tWork开始继续工作,否则就一直等着(即阻滞)。接下来我们看到在主线程中(本例中即UI线程,它相对线程tWork来说,就是一个“另外的线程”):

    autoResetEvent.Set();

    主线程通过上面这句代码负责向在autoResetEvent上等待的线程tWork上下文发送信号,即将tWork的阻滞状态设置为true。tWork接收到这个信号,开始继续工作。

    这个例子相当简单,但是已经完整说明了信号机制的工作原理。


    2:AutoResetEvent和ManualResetEvent的区别

    AutoResetEvent和ManualResetEvent有这样的区别:前者在发送信号完毕后(即调用Set方法),自动将自己的阻滞状态设置为false,而后者需要进行手动设定。可以通过一个例子来说明这种区别:

    AutoResetEvent autoResetEvent =new AutoResetEvent(false);

    privatevoid buttonStartAThread_Click(object sender, EventArgs e)
    {
    StartThread1();
    StartThread2();
    }

    private
  • 相关阅读:
    Linux中断管理 (2)软中断和tasklet
    Linux中断管理 (1)Linux中断管理机制
    Linux中断管理
    Linux内核访问用户空间文件:get_fs()/set_fs()的使用
    Linux进程管理 (1)进程的诞生
    Linux进程管理专题
    Linux内存管理 (23)一个内存Oops解析
    Linux内存管理 (22)内存检测技术(slub_debug/kmemleak/kasan)
    Linux内存管理 (21)OOM
    Linux内存管理 (20)最新更新和展望
  • 原文地址:https://www.cnblogs.com/xyqCreator/p/2626439.html
Copyright © 2020-2023  润新知