多线程编程精髓(二)

基于上篇的多线程编程的基本内容,本篇开始Windows操作系统下各种常用的多线程资源同步对象。

(1)windows线程资源同步之临界区:两个重要的 Windows API 函数 WaitForSingleObject 和 WaitForMultipleObjects,前者是一次操作一个资源同步对象,后者同时操作多个资源

同步对象,区别和注意点如下:

               1.DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); //参数hhandle表示需要等待的内核对象,参数dwMilliseconds 实际上是一个 unsigned long 类

型,设置 INFINITE 表示无限等待,在 Windows 上可以调用WaitForSingleObject等待的常见对象如下表所示:

                       可以被等待的对象                  等待对象成功的含义                                                                         对象类型

                        线程                                         等待线程结束                                                                                    HANDLE

                       Process                                   等待进程结束                                                                                    HANDLE

                       Event(事件)                        等待 Event 有信号                                                                             HANDLE

                       Mutex (互斥体)                       等待持有 Mutex 的线程释放该 Mutex,等待成功,拥有该 Mutex     HANDLE

                       Semaphore(信号量)          等待该 Semaphore 对象有信号                                                          HANDLE

             函数的返回类型有:WAIT_FAILED -调用失败,可以用GetLastError()得到具体的错误码,WAIT_OBJECT_0-表示成功等待到设置的对象,WAIT_TIMEOUT-等待超时

WAIT_ABANDONED-当等待的对象是 Mutex 类型时,如果持有该 Mutex 对象的线程已经结束,但是没有在结束前释放该 Mutex,此时该 Mutex 已经处于废弃状态,其行为是未知的,

不建议再使用。

            2. DWORD WaitForMultipleObjects( DWORD nCount,const HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);  //nCount 指定对象数组的长度,lpHandles表示需要

等待的对象数组指针,bWaitAll表示是否等待所有对象数组有信号,可设置true或者false,设置false只要有一个对象有信号即会返回,在设置bWaitAll 为false的情况下, 除了和上述介绍

的返回值是 WAITFAILED和,,WAITTIMEOUT 以外,由于是多个对象同步,返回值 WAIT_OBJECT_0 to (WAIT_OBJECT_0 + nCount– 1),比如:现在等待三个对象 A1、A2、A3,

它们在数组 lpHandles 中的下标依次是 0、1、2,某次 WaitForMultipleObjects 返回值是 Wait_OBJECT_0 + 1,则表示对象 A2 有信号,导致 WaitForMultipleObjects 调用成功返回。同

理对于WAIT_ABANDONED_0 to (WAIT_ABANDONED_0 + nCount– 1)。

           windows的临界区对象操作API函数:

          void    InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);  //初始化临界区对象

          void    DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);  //销毁临界区对象

          BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); //尝试进入临界区,如果是,返回true,无法进入则阻塞,返回false

          void     EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);  //进入临界区

          void     LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); //离开临界区

         注意:此临界区对象是互斥的,只能一个线程访问持有,为了避免死锁,进入和离开临界区函数需要成对使用,为了避免因函数有多个出口造成的编码疏漏,可使用 RAII 封装临界

区对象类的方法,此API函数为Windows 系统多线程资源同步最常用的对象之一。

(2)windows线程资源同步之Event:Event是windows内核的常用多线程同步的对象,特点是简单易用,但无法精确控制唤醒指定数量的线程。API函数 为:

         HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL   bManualReset, BOOL  bInitialState,LPCTSTR   lpName);    //参数lpEventAttributes 设置Event对象

的安全属性,设置NULL为默认安全属性;参数bManualReset表示设置 Event 对象受信(变成有信号状态)时的行为,设置true要手动调用 ResetEvent 函数去将 Event 重置成无信号

状态,设置false表示Event 事件对象受信后会自动重置为无信号状态;参数 bInitialState 设置 Event 事件对象初始状态是否是受信的,true表示有信号,false表示无信号;参数 lpName 

可以设置 Event 对象的名称,可设置为NULL,Event对象可以通过名称在进程之前不同进程之间共享;返回值为 NULL表示创建失败。

            BOOL  SetEvent(HANDLE hEvent); //设置Event对象从无信号变成有信号状态

            BOOL  ResetEvent(HANDLE hEvent); //设置Event对象从有信号变成无信号状态

(3)windows线程同步之Mutex:windows的Mutex(互斥量)在同一时刻只能属于一个线程,也可以不属于任何线程,具有排他性,创建Mutex可设置它属于的线程,其他线程要获取调

用WaitForSingleObject 进行申请,创建 Mutex 的 API 是 CreateMutex,释放 Mutex 的 API 是ReleaseMutex :

           HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL  bInitialOwner, LPCTSTR  lpName);  //参数 lpMutexAttributes 与Event函数类似,设置 

lpMutexAttribute为NULL,参数 bInitialOwner表示调用创建Mutex的线程是否立即拥有该对象,true为拥有,fasle为不拥有;参数 lpName为Mutex 对象的名称,和Event对象一样,多个

线程之间可通过名称共享;返回值为NULL表示创建失败;

           BOOL ReleaseMutex(HANDLE hMutex); //  参数 hMutex 即需要释放所有权的 Mutex 对象句柄

(4)windows线程同步之Semaphore:Semaphore 也是 Windows 多线程同步常用的对象之一,与Event、Mutex区别不同的是可以信号量存在资源计数,可以精确控制唤醒的线程数

目。创建 Semaphore 对象的 API 函数和增加信号量资源个数的API如下所示:

          HANDLE  CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG  lInitialCount, LONG lMaximumCount,LPCTSTR   lpName);  //参数 

lpSemaphoreAttributes 指定对象的安全属性,设置为NULL;参数 lInitialCount 指定初始可用资源数量,每调用一次 WaitForSingleObject 获得 Semaphore 对象,该对象的资源计数会

减少一个,参数 lMaximumCount 最大资源数量上限,设置大于0,使用ReleaseSemaphore 增加资源个数不能大于这个上限值;参数 lpName 指定 Semaphore 对象的名称,可通过

名称进行跨进程共享;返回值为NULL表示创建失败。

          BOOL  ReleaseSemaphore( HANDLE hSemaphore,LONG  lReleaseCount,  LPLONG lpPreviousCount);  //新增信号量的资源个数个数,参数 hSemaphore 是需要操作的信号量

句柄;参数 lReleaseCount,需要增加的资源数量;参数 lpPreviousCount 是一个 long 型(32 位系统上 4 个字节)的指针,返回上一次资源的数量。

        信号量根据当前资源的个数分配消费者,当资源数量为0时,所有消费者处于挂起状态,当新资源到来时,消费者会被唤醒。

(5)让程序只启动一个实例的方法:使用线程内核同步对象,首次启动这个进程,这个进程会调用 CreateMutex 函数创建一个名称为“MySingleInstanceApp”的互斥体对象。当再次准备

启动一份这个进程时,再次调用 CreateMutex 函数,由于该名称的互斥体对象已经存在,将会返回已经存在的互斥体对象地址,此时通过 GetLastError() 函数得到的错误码是 

ERROR_ALREADY_EXISTS 表示该名称的互斥体对象已经存在,此时我们激活已经存在的前一个实例,然后退出当前进程即可。如下函数实现:

          bool CheckInstance()

        {

               HANDLE hSingleInstanceMutex = CreateMutex(NULL, FALSE, _T("MySingleInstanceApp"));

              if (hSingleInstanceMutex != NULL)

            {

                  if (GetLastError() == ERROR_ALREADY_EXISTS)

                   {

                          return true;

                  }

           }

           return false;

     } 

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 一、上路吧,线程 从Win32角度看,进程含有内存(理论上可达2G)和资源,本身并不能够执行,只是提供一个安置内存...
    赤果_b4a7阅读 756评论 0 1
  • LINUX 基础知识 1、线程的概念 上下文切换 : 运行程序前需要将相应进程信息读入内存,如果运行进程A后需要紧...
    Ycres阅读 752评论 0 2
  • 引用自多线程编程指南应用程序里面多个线程的存在引发了多个执行线程安全访问资源的潜在问题。两个线程同时修改同一资源有...
    Mitchell阅读 1,973评论 1 7
  • 本文根据众多互联网博客内容整理后形成,引用内容的版权归原始作者所有,仅限于学习研究使用,不得用于任何商业用途。 互...
    深红的眼眸阅读 1,092评论 0 0
  • 1.内存的页面置换算法 (1)最佳置换算法(OPT)(理想置换算法):从主存中移出永远不再需要的页面;如无这样的...
    杰伦哎呦哎呦阅读 3,215评论 1 9