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

多线程基础之二:mutex和semaphore使用方法相关推荐

  1. 【Java进阶营】JAVA多线程基础学习二:synchronized

    本篇主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题,没错就是使用synchronized. 一.如何解决线程安全问题? 一般 ...

  2. 多线程基础(二)pthread的了解

    IOS中多线程的实现方案 了解NSOperation(代码) 所有的方法都是pthread开头的 然后再搞一条线程 pthread_create方法有返回值,作用:判断线程创建是否成功? 常用的方法: ...

  3. 多线程同步与并发访问共享资源工具—Lock、Monitor、Mutex、Semaphore

    "线程同步"的含义 当一个进程启动了多个线程时,如果需要控制这些线程的推进顺序(比如A线程必须等待B和C线程执行完毕之后才能继续执行),则称这些线程需要进行"线程同步(t ...

  4. 第十章 进程间的通信 之 Java/Android多线程开发(二)

    文章目录 (一)Java 多线程开发 1.1)线程状态 1.2)线程控制方法 (1.2.1)Synchronized (1.2.2)Volatile (1.2.3)ReentrantLock 1.3) ...

  5. 安琪拉教百里守约学并发编程之多线程基础

    <安琪拉与面试官二三事>系列文章 一个HashMap能跟面试官扯上半个小时 一个synchronized跟面试官扯了半个小时 <安琪拉教鲁班学算法>系列文章 安琪拉教鲁班学算法 ...

  6. Java基础(二):集合、IO流(Zip压缩输入/输出流等)、File文件类、反射、枚举

    Java基础(一):编译和解释.数据类型.变量作用域.String常用方法.数组.面向对象.异常 Java基础(二):集合.IO流(Zip压缩输入/输出流等).File文件类.反射.枚举 Java异常 ...

  7. 【嵌入式操作系统】FreeRTOS信号量mutex和semaphore的区别

    今天学习信号量mutex和semaphore的区别,找到了正点原子的博客介绍,讲的挺详细的.建议大家阅读 转载自:https://blog.csdn.net/nippon1218/article/de ...

  8. Java多线程干货系列—(一)Java多线程基础

    前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学习之旅吧. 正文 线程与进程 1 线程:进程中负责程序执行的 ...

  9. 多线程基础(五)NSThread线程通信

    5.多线程基础 线程间通信 什么叫线程间通信 在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 线程间通信的体现 1个线程传递数据给另一个线程 在1个线程中执行完特定任务后, 线程间 ...

最新文章

  1. Google揭露美国政府通过NSL索要用户资料
  2. react 条件渲染
  3. Linux统计文件行数、字数、字节数
  4. 获取Flex SDK加载进度的方法
  5. Workflow相关表简单分析
  6. Python实现:某个用户登录后,查看自己拥有所有权限
  7. xdf文件改word_真正Txt 文本文件和Doc Word文件批量互转工具
  8. python和c 的区别-C++/C/JAVA/Python之间的区别?
  9. M1芯片Macbook最简单从11.3降级到11.2.3教程
  10. 微信公号“架构师之路”学习笔记(二)-高可用高并发负载均衡的架构设计(冗余+自动故障转移、水平扩展等)
  11. java web图书管理_基于Javaweb的图书管理系统
  12. cad有没有网页版_AutoCAD
  13. matlab中blur函数_matlab-----均值滤波函数的实现
  14. 操作系统课程设计1_Linux系统
  15. 软件工程师能力自我评价表
  16. 华硕h81m一k跳线图_主板跳线接法
  17. 华为鸿蒙亮利剑,华为亮利剑,超级主镜头+鸿蒙OS,所有期许或将如愿以偿
  18. python---之cython的使用
  19. 易语言 服务端给客户端发弹窗信息源码
  20. 哈尔滨工程大学-济海追风-声音信标

热门文章

  1. 怎么在VS Code中配置C/C++开发环境?
  2. 商标权的主要内容是什么
  3. 斐讯phicomm原厂固件桥接AP设置图文教程
  4. UDP传输报文大小详解
  5. JavaXml教程(七)使用JDOM修改XML文件内容
  6. 一个真实而又令人震撼的故事
  7. mysql学习经验学习资料下载
  8. R语言实战:机器学习与数据分析源代码6(最终弹)
  9. Service Unavailable 问题
  10. 基于JAVA的葫芦娃 — 最终之战