windows平台多线程同步之Mutex的应用
来源:互联网 发布:淘宝鞋子质量好的店铺 编辑:程序博客网 时间:2024/05/17 12:03
WINBASEAPIHANDLEWINAPICreateThread( 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;}
执行结果
通过测试可知,以上互斥对象的引入可以很好的解决线程间访问资源同步的问题。关于互斥对象,还有以下几个问题需要说明。
- 互斥对象的释放问题
如果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进行介绍和解析,敬请关注。文中如有谬误,还望不吝赐教。
点击博客新址
1 1
- windows平台多线程同步之Mutex的应用
- windows平台多线程同步之Event的应用
- QNX多线程同步之Mutex
- 多线程之线程同步Mutex
- C++多线程同步之Mutex(互斥量)
- Windows线程同步之互斥锁(Mutex)
- Windows 平台下的同步机制 (2)– 互斥体(Mutex)
- Windows 平台下的同步机制 (2)– 互斥体(Mutex)
- Windows 平台下的同步机制 (2)– 互斥体(Mutex)
- 多线程--同步(Mutex互斥量)
- 用mutex同步多线程
- 多线程同步-mutex
- 多线程同步Mutex
- 多线程编程(10) - 多线程同步之 Mutex (互斥对象)
- 多线程编程(10) - 多线程同步之 Mutex (互斥对象)
- 多线程编程(10) - 多线程同步之 Mutex (互斥对象)
- Windows多线程编程(4)同步对象——Mutex对象
- Windows 多线程 Mutex
- vim 复制
- cocos2d-js(一)引擎的工作原理和文件的调用顺序
- win32汇编 数据类型及定义
- 在樱花vps上安装win系统(下)
- 有趣的数
- windows平台多线程同步之Mutex的应用
- 自定义二级联动
- 【算法设计与分析基础】高斯消元
- Android Application详解
- Java_GUI中GridBagLayout布局使用详解
- 带头节点的单链表
- 很有用的stl string类
- js 返回页面顶部
- 从大量整数中选取最小/大的若干个