当前位置: 首页 > article >正文

多线程基础之二:mutex和semaphore使用方法

mutex和semaphore都是内核对象,是用来实现多进程间或多线程锁机制的基础。本文将要介绍两者的使用方式。


0. 多线程锁机制涉及的Windows API

创建mutex内核对象,用来作为线程间或进程间通信的API接口如下

HANDLE  WINAPI CreateMutex( __in_opt LPSECURITY_ATTRIBUTES lpMutexAttributes, __in BOOL bInitialOwner, __in_opt LPCTSTR lpName);//lpMutexAttributes:第一个参数表示安全控制,一般直接传入NULL
//bInitialOwner:第二个参数用来确定互斥量的初始拥有者。如果传入TRUE表示互斥量对象内部会记录创建它的线程ID,并将递归计数置为1,由于该线程ID非零,所以互斥量处于未触/无信号/未通知状态,表示互斥量为创建线程拥有;
如果传入False,那么互斥量对象内部的线程ID号设置为NULL,递归计数设置为0,这意味着互斥量不为任何线程占用,处于触发/有信号/已通知状态。
//lpName:第三个参数用来设置互斥量的名称,多线程就是通过名称来确保它们访问的是同一个互斥量。
(Mutex不仅可以多线程使用,也可以跨进程使用,所以其名称对于整个系统而言是全局的,故而起名字时尽量复杂)。

既然是关于多线程的锁使用问题,那么显然需要介绍下创建线程的API,函数原型:

HANDLE  WINAPI  CreateThread(LPSECURITY_ATTRIBUTES    lpThreadAttributes,SIZE_T     dwStackSize,LPTHREAD_START_ROUTINE  lpStartAddress,LPVOID    lpParameter,DWORD    dwCreationFlags,LPDWORD  lpThreadId
);
函数说明:
第一个参数表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
第二个参数表示线程栈空间大小。传入0表示使用默认大小(1MB)。
第三个参数表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址,传入函数名即可。
第四个参数是传给线程函数的参数。
第五个参数指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。
第六个参数将返回线程的ID号,传入NULL表示不需要返回该线程ID号。函数返回值:
成功返回新线程的句柄,失败返回NULL。通过该API的返回值HANDLE,可以看到操作系统将函数、线程、进程、文件、窗口等都统一看作“文件”对象,故而都可以使用Handle统一来指引到目的地址。

既然是使用锁机制,那么显然需要介绍锁的获取方式,即acquire(lock)的应该采用的API,其中最为重要的莫过于WaitForSingleObject和WaitForMultipleObjects两个API。

WaitForSingleObject
函数功能:等待函数 – 使线程进入等待状态,直到指定的内核对象被触发。
函数原形:
DWORD  WINAPI  WaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds
);
函数说明:
第一个参数为要等待的内核对象,该内核对象只需支持信号通知即可,如event, mutex, process, thread, semaphore。
第二个参数为最长等待的时间,以毫秒为单位,如传入5000就表示5秒,传入0就立即返回,传入INFINITE表示无限等待。
WaitForMultipleObjects
是Windows中的一个功能非常强大的函数,几乎可以等待Windows中的所有的内核对象
函数原型为:
DWORD WaitForMultipleObjects(  DWORD nCount,             // number of handles in the handle array  CONST HANDLE *lpHandles,  // pointer to the object-handle array  BOOL fWaitAll,            // wait flag  DWORD dwMilliseconds      // time-out interval in milliseconds  
);  
参数解析:
DWORD 就是 Double Word, 每个word为2个字节的长度,DWORD双字即为4个字节,每个字节是8位。
nCount  指定列表中的句柄数量,最大值为MAXIMUM_WAIT_OBJECTS(64)  
*lpHandles 句柄数组的指针。lpHandles为指定对象句柄组合中的第一个元素 HANDLE类型可以为(Event,Mutex,Process,Thread,Semaphore)数组  
bWaitAll 等待的类型,如果为TRUE,表示除非对象都发出信号,否则就一直等待下去;如果FALSE,表示任何对象发出信号即可 
dwMilliseconds指定要等候的毫秒数。如设为零,表示立即返回。如指定常数INFINITE,则可根据实际情况无限等待下去。

关于信号量semaphore的介绍较为复杂,是因为信号量使用的场景更为复杂。首先介绍创建信号量的API接口函数

HANDLE WINAPI CreateSemaphore( _In_opt_  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes _In_      LONG lInitialCount, _In_      LONG lMaximumCount, _In_opt_  LPCTSTR lpName 
);
第一个参数:安全属性,如果为NULL则是默认安全属性 
第二个参数:信号量的初始值,要>=0<=第三个参数 
第三个参数:信号量的最大值,即最大资源数目 
第四个参数:信号量的名称,一般不涉及到跨进程使用基本都是输入NULL的。 
返回值:指向信号量的句柄,如果创建的信号量和已有的信号量重名,那么返回已经存在的信号量句柄
ReleaseSemaphore
函数功能:为指定的信号量增加指定的资源计数
HANDLE  ReleaseSemaphore (
HANDLE  hSemaphore,
LONG     lReleaseCount,
PLONG    plPreviousCount
);
第一个参数:目标信号量句柄
第二个参数:本次释放指定的增加资源计数
第三个参数:返回当前资源计数的原始值,若设置NULL,则不返回。

信号量的使用方法或流程如下:
1、创建一个信号量:CreateSemaphore;
2、使用信号量名字,打开一个已经存在的信号量:OpenSemaphore;
3、监听并获得信号量的一个占有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数……(可能造成阻塞);
4、释放信号量的占有权,增加信号量的资源计数:ReleaseSemaphore;
5、关闭信号量,将其从内核中删除:CloseHandle;

信号量semaphore的使用规则:
1. 如果当前资源计数大于0,即信号量的可用资源数大于0,那么信号量处于触发状态,可响应任何监听获取请求;
2. 如果当前资源计数等于0,那么信号量处于未触发状态;那么系统会让调用线程进入等待状态。
CreateSemaphore(NULL,0,1,NULL); 当第二个参数为0时,表示当前信号量的可用资源数目为0,表示创建进程在创建信号量之时并占用了该信号量,其余监听信号量的线程就会进入等待状态,直到创建信号量的宿主线程释放信号量。
3. 当前资源计数绝对不会大于最大资源计数。


1. Mutex的多线程锁机制使用案例

#include <iostream>
#include <windows.h>
#include <time.h>
using namespace std;HANDLE  g_hMutex = NULL;
const int g_Number = 3; //代表线程对象的数目
DWORD WINAPI ThreadProc1(__in  LPVOID lpParameter);
DWORD WINAPI ThreadProc2(__in  LPVOID lpParameter);
DWORD WINAPI ThreadProc3(__in  LPVOID lpParameter);int main()
{clock_t start_time = clock();//g_hMutex = CreateMutex(NULL,FALSE,NULL);//TRUE代表主线程拥有互斥对象 但是主线程没有释放该对象  互斥对象谁拥有 谁释放 g_hMutex = CreateMutex(NULL,TRUE,NULL);printf("主线程创建时便占有了互斥对象,没有释放,所以其他子线程无法使用。\n");ReleaseMutex(g_hMutex);printf("主线程释放了互斥对象,其他子线程可以开始使用。\n");// FLASE代表当前没有线程拥有这个互斥对象HANDLE hThread[ g_Number ] = {0};int first = 1, second = 2, third = 3;hThread[ 0 ] = CreateThread(NULL,0,ThreadProc1,(LPVOID)first,0,NULL);hThread[ 1 ] = CreateThread(NULL,0,ThreadProc2,(LPVOID)second,0,NULL);hThread[ 2 ] = CreateThread(NULL,0,ThreadProc3,(LPVOID)third,0,NULL);WaitForMultipleObjects(g_Number,hThread,TRUE,INFINITE); //INFINITE代表一直等下去,该位单位为msCloseHandle( hThread[0] );CloseHandle( hThread[1] );CloseHandle( hThread[2] );CloseHandle( g_hMutex );clock_t end_time = clock();cout<<"Running time is:"<<static_cast<double>(end_time - start_time)/CLOCKS_PER_SEC*1000<<"ms"<<endl;return 0;
}DWORD WINAPI ThreadProc1(__in  LPVOID lpParameter)
{WaitForSingleObject(g_hMutex, INFINITE);//等待互斥量cout<<(int)lpParameter<<endl;ReleaseMutex(g_hMutex);//释放互斥量return 0;
}DWORD WINAPI ThreadProc2(__in  LPVOID lpParameter)
{WaitForSingleObject(g_hMutex, INFINITE);//等待互斥量cout<<(int )lpParameter<<endl;ReleaseMutex(g_hMutex);//释放互斥量return 0;
}DWORD WINAPI ThreadProc3(__in  LPVOID lpParameter)
{WaitForSingleObject( g_hMutex, INFINITE);//等待互斥量cout<<(int)lpParameter<<endl;ReleaseMutex(g_hMutex);//释放互斥量return 0;
}



2. semaphore的多线程锁机制使用案例

#include <iostream> 
#include <windows.h>
#include <time.h> 
using namespace std;const int g_Number = 3; 
DWORD WINAPI ThreadProc1(__in  LPVOID lpParameter); 
DWORD WINAPI ThreadProc2(__in  LPVOID lpParameter); 
DWORD WINAPI ThreadProc3(__in  LPVOID lpParameter);HANDLE hSemp1,hSemp2,hSemp3;int main() 
{ clock_t start_time = clock();hSemp1 = CreateSemaphore(NULL,0,3,NULL);printf("信号量初始便被主线程占用,需要主线程释放后,其余线程才能使用\n");char ch = getchar();ReleaseSemaphore(hSemp1,1,NULL);printf("信号量可能使得多个进程并行,交替执行。\n");//hSemp2 = CreateSemaphore( NULL,1,1,NULL); //hSemp3 = CreateSemaphore(NULL,1,1,NULL);HANDLE hThread[ g_Number ] = {0}; int first = 1, second = 2, third = 3; hThread[ 0 ] = CreateThread(NULL,0,ThreadProc1,(LPVOID)first,0,NULL); hThread[ 1 ] = CreateThread(NULL,0,ThreadProc2,(LPVOID)second,0,NULL); hThread[ 2 ] = CreateThread(NULL,0,ThreadProc3,(LPVOID)third,0,NULL);WaitForMultipleObjects(g_Number,hThread,TRUE,INFINITE); CloseHandle( hThread[0] ); CloseHandle( hThread[1] ); CloseHandle( hThread[2] );CloseHandle( hSemp1 ); CloseHandle( hSemp2 ); CloseHandle( hSemp3 ); clock_t end_time = clock();cout<<"Running time is:"<<static_cast<double>(end_time - start_time)/CLOCKS_PER_SEC*1000 <<"ms"<<endl;return 0; 
}DWORD WINAPI ThreadProc1(__in  LPVOID lpParameter) 
{ WaitForSingleObject(hSemp1, INFINITE);//等待信号量 cout<<(int)lpParameter<<endl; ReleaseSemaphore(hSemp1,1,NULL);//释放信号量 return 0; 
}DWORD WINAPI ThreadProc2(__in  LPVOID lpParameter) 
{ WaitForSingleObject(hSemp1, INFINITE);//等待信号量 cout<<(int )lpParameter<<endl; ReleaseSemaphore(hSemp1,1,NULL);//释放信号量 return 0; 
}DWORD WINAPI ThreadProc3(__in  LPVOID lpParameter) 
{ WaitForSingleObject( hSemp1, INFINITE);//等待信号量 cout<<(int)lpParameter<<endl; ReleaseSemaphore(hSemp1,1,NULL);//释放信号量 return 0; 
}


http://www.lryc.cn/news/2414449.html

相关文章:

  • 移位寄存器及其应用
  • 赛门铁克端点防护(英語:)是由博通公司开发的安全软件套件,包括杀毒软件、入侵检测系统和防火墙,适用于服务器和台式电脑,在端点安全产品中拥有最大的市场份额。
  • 详细的图文Windows电脑设置自动关机/计划关机
  • WDM驱动模型简介
  • Flex4中文字幕教学视频(翻译自Adobe开发者中心)+ 离线下载播放器
  • 苹果电脑是“监狱”、弃用 Ubuntu,GNU 创始人斯托曼谈自由软件运动现状
  • 海龟绘图小案例(内含源码)
  • 针对没有光驱,NTLDR is missing系统无法启动的解决办法
  • 虚幻引擎3(Unreal Engine 3)概要
  • C 语言(基础笔记)
  • 最新d3dx9_33.dll文件丢失解决
  • 《社交网络》中Facemash算法分析
  • Android 版本区别
  • 网络通信原理——OSI模型、TCP/IP模型、数据通信原理
  • 常见的用户密码在后台数据库中加密存储方式
  • 办公楼综合布线系统详细设计方案
  • 路由器的类型及衡量路由器性能的主要参数指标
  • html引入html include_CSS的引入方式笔记
  • 为什么在Spring应用中不建议使用属性注入(Field Injection)
  • 本地代码与托管代码
  • mysqlbinlog -v与-vv --base64-output 与不加的区别
  • 海外代理IP如何获取?
  • 个人博客系统源码 溯雪Sxlog轻博客源码 PHP开源 简洁干净轻博客源码
  • WinPE能破解Windows的密码,这样Windows就不安全吗?
  • 十种不同风格的网站导航菜单
  • nagios安装教程
  • jQuery.validationEngine.js学习
  • 网站建设经验分享:如何进行网站内容更新与维护?
  • MYSQL入门-mysql的下载与安装(1)
  • 文件粉碎机(file pulverizer) v4.2 怎么用