【转】windows平台多线程同步之Mutex的应用

时间:2022-11-03 21:55:04
  • 线程组成: 
    1. 线程的内核对象,操作系统用来管理该线程的数据结构。
    2. 线程堆栈,它用于维护线程在执行代码时需要的所有参数和局部变量。

  操作系统为每一个运行线程安排一定的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);表名该线程放弃执行的权利,操作系统会选择另外的线程进行执行。所以执行结果是:

【转】windows平台多线程同步之Mutex的应用

执行结果

  可以看见程序不是按照预期的效果执行的,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
【转】windows平台多线程同步之Mutex的应用

执行结果

  通过测试可知,以上互斥对象的引入可以很好的解决线程间访问资源同步的问题。关于互斥对象,还有以下几个问题需要说明。

  • 互斥对象的释放问题 
      如果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的应用的更多相关文章

  1. C&plus;&plus;多线程同步之Mutex&lpar;互斥量&rpar;

    原文链接: http://blog.csdn.net/olansefengye1/article/details/53086141 一.互斥量Mutex同步多线程 1.Win32平台 相关函数和头文件 ...

  2. c&num; 多线程同步之Mutex

    说起Mutex,它的中文名字叫互斥体.它是WaitHandle家族成员之一,前面有一篇介绍过WaitHandle的家族成员构成.那么Mutex有什么作用呢?它是怎么使用的? 我们先来看看它的使用场景一 ...

  3. Windows多线程同步系列之一-----互斥对象

    多线程同步之互斥对象 作者:vpoet mail:vpoet_sir@163.com   对卖票问题进行线程间同步,本文将在上文的基础上,使用互斥对象对线程进行同步. 首先看看windows API ...

  4. windows多线程同步

    概述 任何单个应用程序都不能完全使该处理器达到满负荷.当一个线程遇到较长等待时间事件时,同步多线程还允许另一线程中的指令使用所有执行单元.例如,当一个线程发生高速缓存不命中,另一个线程可以继续执行.同 ...

  5. 基于Windows平台的Python多线程及多进程学习小结

    python多线程及多进程对于不同平台有不同的工具(platform-specific tools),如os.fork仅在Unix上可用,而windows不可用,该文仅针对windows平台可用的工具 ...

  6. Windows多线程同步系列之二-----关键区

    关键区对象为:CRITICAL_SECTION 当某个线程进入关键区之后,其他线程将阻塞等待,知道该线程释放关键区的拥有权. 关键区同步主要有以下几个API 初始化关键区对象,无返回值,传入一个关键区 ...

  7. 总结windows多线程同步互斥

    windows多线程同步互斥--总结 我的windows多线程系列文章: windows多线程--原子操作 windows多线程同步--事件 windows多线程同步--互斥量 windows多线程同 ...

  8. windows多线程同步互斥--总结

    我的windows多线程系列文章: windows多线程--原子操作 windows多线程同步--事件 windows多线程同步--互斥量 windows多线程同步--临界区 windows多线程同步 ...

  9. windows多线程同步--临界区

    推荐参考博客:秒杀多线程第五篇 经典线程同步 关键段CS   关于临界区的观念,一般操作系统书上面都有. 适用范围:它只能同步一个进程中的线程,不能跨进程同步.一般用它来做单个进程内的代码快同步,效率 ...

随机推荐

  1. ABP中使用Redis Cache&lpar;1&rpar;

    本文将讲解如何在ABP中使用Redis Cache以及使用过程中遇到的各种问题.下面就直接讲解使用步骤,Redis环境的搭建请直接网上搜索. 使用步骤: 一.ABP环境搭建 到http://www.a ...

  2. struts2总结三:struts2配置文件struts&period;xml的简单总结

    一.struts中的常量constant的配置. 在struts2中同一个常量的配置有三种方式,第一种在struts.xml中,第二种在struts.properties中配置,第三种在web.xml ...

  3. NOIP2010 机器翻译

    1机器翻译 题目背景 小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章. 题目描述 这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换.对于每个英文单 ...

  4. 自定义solr的search UI

    solr使用apache的velocity来定义UI,在solr的search ui的基础上更改即可,主要改\example\solr\collection1\conf\velocity里的文件. 详 ...

  5. Java语言程序设计-Markdown格式作业模板

    Markdown格式作业模板如下,大家可以直接复制粘贴使用. 注意:作业中不能只写答案,题目本身也要出现.. # 1. 本章学习总结 你对于本章知识的学习总结 # 2. 书面作业 **Q1 java ...

  6. &lpar;59&rpar;Wangdao&period;com第十天&lowbar;JavaScript 对象在 栈和堆

    对象的属性值 如果要使用特殊的属性名,需 对象["属性名"] = 属性值       // 存 对象["属性名"]       // 取 obj["1 ...

  7. No X11 DISPLAY variable was set

    在命令行调用图形化界面时报错 “No X11 DISPLAY variable was set” 首先使用xclock命令查看是否能调出时钟,如果不行,使用如下命令: 打开xmanager – pas ...

  8. docker 小结

    1.docker修改镜像地址: /etc/docker/daemon.json 2.docker 启动容器: docker run -t -i ubuntu:14.04 /bin/bash 3.查看容 ...

  9. 黄聪:Pjax 无刷新开发web,更好用户体验

    什么Ajax.Pjax.Njax...神马玩意? 有Njax吗? 木有...不过真有Pjax!! 其实pjax就是用到了html5的新history api: pushState和replaceSta ...

  10. mybatis &dollar;&lbrace;&rcub;与&num;&lbrace;&rcub;的区别

    #{}可以直接获取方法的参数: ${}从方法的参数中利用get方法来获取参数的属性值: