【转】windows平台多线程同步之Mutex的应用
- 线程组成:
- 线程的内核对象,操作系统用来管理该线程的数据结构。
- 线程堆栈,它用于维护线程在执行代码时需要的所有参数和局部变量。
操作系统为每一个运行线程安排一定的CPU时间 —— 时间片。系统通过一种循环的方式为线程提供时间片,线程在自己的时间内运行,多个线程不断地切换运行,因时间片相当短,因此,给用户的感觉,就好像线程是同时运行的一样。
单cpu计算机一个时间只能运行一个线程,如果计算机拥有多个CPU,线程就能真正意义上同时运行了。
windows平台下,创建线程可以使用windows api 函数CreateThread来实现,函数声明是:
WINBASEAPI
HANDLE
WINAPI
CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);
参数说明:
lpThreadAttributes | 线程安全性,使用缺省安全性,一般缺省null |
dwStackSize | 堆栈大小,0为缺省大小 |
lpStartAddress | 线程要执行的函数指针,即入口函数 |
lpParameter | 线程参数 |
dwCreationFlags | 线程标记,如为0,则创建后立即运行 |
lpThreadId | LPDWORD为返回值类型,一般传递地址去接收线程的标识符,一般设为null |
因为要使用windows api函数,所以包含:
#include <windows.h>
另外需要标准输入输出函数,所以包含:
#include <iostream.h>
- 问题引出 以多个售票窗口卖同一张火车票为例,定义一个全局的票数tickets,用两个线程来执行卖票,两个线程访问同一个变量tickets,先看一个不正确的写法:
//问题程序#include <windows.h>
#include <iostream.h>DWORD WINAPI Fun1Proc(LPVOID lpParameter
);DWORD WINAPI Fun2Proc(LPVOID lpParameter
);int tickets=100; void main() { HANDLE hThread1; HANDLE hThread2; hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); CloseHandle(hThread1); CloseHandle(hThread2); system("pause"); } DWORD WINAPI Fun1Proc( LPVOID lpParameter ) { while(TRUE) { if(tickets>0) { Sleep(1);//假定为卖票需要花费的时间 cout<<"thread1 sell ticket : "<<tickets--<<endl; } else break; } return 0; } DWORD WINAPI Fun2Proc( LPVOID lpParameter // thread data ) { while(TRUE) { if(tickets>0) { Sleep(1); cout<<"thread2 sell ticket : "<<tickets--<<endl; } else break; } return 0; }
线程中sleep(1);表名该线程放弃执行的权利,操作系统会选择另外的线程进行执行。所以执行结果是:
执行结果
可以看见程序不是按照预期的效果执行的,tickets的改变是混乱的。所以两个线程访问同一块资源时,需要考虑线程同步问题,即其中一个线程操作改资源时,其他线程不能访问该资源,只能等待,该线程执行结束之后,其他线程才能对该资源进行访问。
一般采用互斥对象来实现线程的同步。
- 互斥对象
特征:
互斥对象(mutex)属于内核对象,它能够确保线程拥有对单个资源的互斥访问权。
互斥对象包含一个使用数量,一个线程ID和一个计数器。
ID用于标识系统中的哪个线程当前拥有互斥对象,计数器用于指明该线程拥有互斥对象的次数。
采用互斥对象进行多线程同步的正确例子如下:
#include <windows.h>
#include <iostream.h>DWORD WINAPI Fun1Proc(LPVOID lpParameter // thread data
);DWORD WINAPI Fun2Proc(LPVOID lpParameter // thread data
);
int index=0; int tickets=100; HANDLE hMutex; void main() { HANDLE hThread1; HANDLE hThread2; hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); CloseHandle(hThread1); CloseHandle(hThread2); //创建一个匿名的互斥对象,且为有信号状态, hMutex=CreateMutex(NULL,FALSE,NULL); system("pause"); } DWORD WINAPI Fun1Proc( LPVOID lpParameter // thread data ) { while(TRUE) { //等待互斥对象的信号,INFINITE表示一直等待,对之后的代码进行保护 WaitForSingleObject(hMutex,INFINITE); if(tickets>0) { Sleep(1); cout<<"thread1 sell ticket : "<<tickets--<<endl; } else break; //释放指定互斥对象的所有权,操作系统将互斥对象的线程id被置为0,互斥对象变为已通知状态,线程2就能请求到互斥对象 ReleaseMutex(hMutex); } return 0; } DWORD WINAPI Fun2Proc( LPVOID lpParameter // thread data ) { while(TRUE) { WaitForSingleObject(hMutex,INFINITE); if(tickets>0) { Sleep(1); cout<<"thread2 sell ticket : "<<tickets--<<endl; } else break; ReleaseMutex(hMutex); } return 0; }
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
执行结果
通过测试可知,以上互斥对象的引入可以很好的解决线程间访问资源同步的问题。关于互斥对象,还有以下几个问题需要说明。
- 互斥对象的释放问题
如果main中
hMutex=CreateMutex(NULL,TRUE,NULL);
子线程中:
while(TRUE){ReleaseMutex(hMutex);//无效//等待互斥对象的信号,INFINITE表示一直等待,对之后的代码进行保护WaitForSingleObject(hMutex,INFINITE); }
如果CreateMutex第二个参数为true,则表示主线程拥有该互斥对象,操作系统将互斥对象的线程id设为主线程的线程id,如果主线程不释放,则子线程会一直等待,此时子线程也没有权利进行释放,所以使用互斥对象的原则是:谁拥有互斥对象,谁释放互斥对象。
另外,如果main中
hMutex=CreateMutex(NULL,TRUE,NULL);
并且再次请求互斥对象:
WaitForSingleObject(hMutex,INFINITE);
并调用一次释放互斥对象:
ReleaseMutex(hMutex);
此时子线程依然是等待状态,得不到互斥对象的使用权,原因是:
CreateMutex(NULL,TRUE,NULL)由于第二个参数为true,主线程拥有互斥对象的使用权,互斥对象内部计数器加1,再次调用WaitForSingleObject请求互斥对象时,内部计数器又加1,计数器是记录线程拥有互斥对象的次数,而只释放ReleaseMutex了一次,互斥对象依然被占用,所以子线程得不到使用权。
因此正确的写法是:
hMutex=CreateMutex(NULL,TRUE,NULL);WaitForSingleObject(hMutex,INFINITE);ReleaseMutex(hMutex);ReleaseMutex(hMutex);
如果多次请求互斥对象,就应该多次释放互斥对象。
再看这样一种情况,线程中没有释放互斥对象的拥有权:
DWORD WINAPI Fun1Proc(LPVOID lpParameter){waitforsingleobject(hmutex,infinite);cout<<"thread1 is running"<<endl;return 0;}DWORD WINAPI Fun2Proc(LPVOID lpParameter){waitforsingleobject(hmutex,infinite);cout<<"thread2 is running"<<endl; return 0; }
此时执行依然能够得到输出:
"thread1 is running"thread2 is running
这是因为:如果线程退出时没有释放互斥对象,操作系统在销毁线程时会自动将线程占用的互斥对象的信息清除,计数器归零,这样其他线程(thread2 )就能申请到互斥对象使用权。
- 创建命名互斥对象
hMutex=CreateMutex(NULL,TRUE,"myApp");if(hMutex){if(ERROR_ALREADY_EXISTS==GetLastError()) { cout<<"已经有一个相同应用程序在运行!"<<endl; return; } }
命名互斥对象的一种应用是:通过命名互斥对象,可以保证当前只有一个应用程序实例在运行。
以上是关于windows平台下多线程同步相关的互斥对象的使用问题,之后将对线程同步的事件对象Event进行介绍和解析,敬请关注。文中如有谬误,还望不吝赐教。
【转】windows平台多线程同步之Mutex的应用相关推荐
- windows系统多线程同步机制原理总结
windows系统多线程同步机制原理总结 同步问题是开发过程中遇到的重要问题之一.同步是要保证在并发执行的环境中各个控制流可以有序地执行,包括对于资源的共享或互斥访问,以及代码功能的逻辑顺序. 为了保 ...
- QNX多线程同步之Mutex
之前提到了QNX上的线程创建方法,现在进一步学习QNX上多线程的同步.曾经编写过多线程应用的同学们都知道线程之间的同步在多线程环境中特别重要,线程之间如果没有同步好,经常会出现逻辑错误. 有关QNX线 ...
- 多线程编程(4) - 多线程同步之 Mutex (互斥对象)
原理分析: 互斥对象是系统内核对象, 各线程都可以拥有它, 谁拥有谁就能执行; 执行完毕, 用 ReleaseMutex 函数释放拥有权, 以让其他等待的线程使用. 其他线程可用 WaitForSin ...
- windows平台goldgate同步oracle数据库
一.环境 操作系统 Microsoft Windows Server 2003 R2 Enterprise Edition Service Pack 2 数据库版本: Oracle Database ...
- java迭代器创建后mutx锁,多线程编程(10) - 多线程同步之 Mutex (互斥对象)
原理分析: 互斥对象是系统内核对象, 各线程都可以拥有它, 谁拥有谁就能执行; 执行完毕, 用 ReleaseMutex 函数释放拥有权, 以让其他等待的线程使用. 其他线程可用 WaitForSin ...
- Windows中多线程同步之事件(Event)
可用事件来当做同步的依据,需使用以下函数 CreatEvent():创建一个事件对象 SetEvent():把指定事件对象的状态设置为发信号状态 ResetEvent():把指定事件对象的状态设置为不 ...
- Windows平台使用CreateProcess实现多进程同步启动
思路 由于目前做音视频方面的内容,有些时候会遇到音频文件和视频文件分成两个文件,需要手动同步播放.专业的音视频处理软件又不会用,只会用ffmpeg.但ffmpeg要用命令行,而且要一个一个文件打开,同 ...
- windows多线程同步--临界区
推荐参考博客:秒杀多线程第五篇 经典线程同步 关键段CS 关于临界区的观念,一般操作系统书上面都有. 适用范围:它只能同步一个进程中的线程,不能跨进程同步.一般用它来做单个进程内的代码快同步,效率比较 ...
- 通过outlook邮箱实现windows、安卓、ios三平台日历同步
2020年10月23日 有人点赞+评论了我这篇文章,挺被触动的...不是因为感觉自己写的文章有用,而是因为感到非常讽刺,因为这是我写的最最最随意的一篇文章了,但是它的阅读量是我: 写的最认真的系列&l ...
最新文章
- 029_自己实现一个HashMap
- java.lang.NoSuchMethodError
- VTK:图片之ImageToPolyDataFilter
- python字符串前面去两位_在Python 3中删除字符串文字前面的'b'字符do
- 饿了么2020外卖备注图鉴:12个关键词覆盖我们的生活
- ubuntu中bash,sh,./,bash区别
- NUC1742 Subsequence【前缀和+二分搜索+尺取法】
- 奈奎斯特定理和香农定理之科普篇
- Python3选择支持非ASCII码标识符的缘由
- ifix 读写mysql_[转载]vb6读取ifix实时数据库和历史数据库
- linux下测硬盘读写速率,linux下测试硬盘读写速度 互联网技术圈 互联网技术圈
- Python BS4 星巴克界面图片报错, tfp = open(filename, ‘wb‘) FileNotFoundError: [Errno 2] No such file or
- 操作MySQL出错提示“BLOB/TEXT column used in key specification without a key length”解决办法
- 使用React.js和appbase.io构建类似Twitter的Search Feed
- POJ 1144 Network 图论
- Java开发 - SpringCache初体验
- 猿辅导编程python_猿辅导旗下品牌猿编程,宣布成立少儿编程研究院
- PCIe学习笔记(一)-------1.3 PCIe数据包(TLP,DLLP,PLP)
- 关于win10安装PingFangThin字体后换不回来的问题
- Java 的设计模式
热门文章
- 从零开始学习 webservice第一集,java webservice简单实例入门教程
- Chapter 17 高级进程间通信
- XML Programming with C# and .NET
- vue.js接收并下载文件流(blob对象)
- 5分钟Serverless实践 | 构建无服务器的敏感词过滤后端系统
- MySQL启动关闭添加到 /etc/init.d/mysqld
- 2016年全球电信市场资本支出略增0.7%至3140亿美元
- rac安装grid报INS-41112错误
- iptables 实现地址转换与安全控制
- 中医治疗慢性病很有效