Windows 互斥对象在线程同步上的运用

互斥对象在线程同步时的使用

1 多线程在资源共享的时候出现的问题

在程序中如果不同线程对同一个对象进行操作的话就有可能出现因为线程切换而导致的问题。例如下面的程序

#include <stdio.h>
#include <WinSock2.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

DWORD WINAPIfun1Proc(LPVOID lpParameter);
DWORD WINAPIfun2Proc(LPVOID lpParameter);

int index = 0;
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);
         Sleep(4000);
}

DWORD WINAPIfun1Proc(LPVOID lpParameter)
{
         while(TRUE)
         {
                   if(tickets>0)
                   {
                            cout<<"Thread1 sell ticket:"<<tickets--<<endl;
                   }
                   else
                            break;
         }
         return0;
}

DWORD WINAPIfun2Proc(LPVOID lpParameter)
{
         while(TRUE)
         {
                   if(tickets>0)
                   {
                            cout<<"Thread2 sell ticket:"<<tickets--<<endl;
                   }
                   else
                            break;
         }
         return0;
}

我们期望的目标是线程1和线程2都一起递减,直到为减到1为止。但是很有可能出现这样的情况:线程1中在if进行判断的时候tickets=1,然后就发生了线程切换,此时tickets的值还没有递减,所以在线程2中判断为真,然后tickets--,tickets = 0,此时线程再发生切换到线程1的话,那么就会出现tickets--,使tickets变成了0,这就不是我们期望的效果了。

2 问题的解决方案:WaitForSingleObject

很容易想到如果把if-else作为一个整体执行不被打断那就好了。这种想法是正确的,于是就有了WaitForSingleObject函数的出现。

DWORD WINAPI WaitForSingleObject(
 _In_  HANDLE hHandle,
 _In_  DWORD dwMilliseconds
);

<1>这个函数是这样的,只要句柄所指向的内核对象时无信号的,那么这个函数就会一直阻塞下去除非时间经过了dwMilliseconds。所以它就像一个哨兵,必须给出通行证才能过去,没有的话那就一直等着吧。

<2>句柄可以的类型

  • Change notification
  • Console input
  • Event
  • Memory resource notification
  • Mutex
  • Process
  • Semaphore
  • Thread
  • Waitable timer

3 互斥对象Mutex在线程同步上的运用

<1>对象的创建

Mutex他能确保线程对单个资源访问的互斥权。这个内核对象包含一个使用计数,一个线程ID,一个计数器。

HANDLE WINAPI CreateMutex(
  _In_opt_  LPSECURITY_ATTRIBUTES lpMutexAttributes,
  _In_    BOOL         bInitialOwner,
  _In_opt_  LPCTSTR      lpName
);

lpMutexAttributes:指定安全性,当为NULL,表示采用默认安全性

bInitialOwner:为TRUE表示创建这个对象的线程具有该互斥对象的所有权,反之则不具有

lpName:互斥对象的名称,如果为NULL则表示一个匿名的互斥对象。如果指定名字,并且在CreateMutex调用之前,该命名的互斥对象已经存在,那么函数将返回这个已经存在的互斥对象的句柄,调用GetLastError的话,会得到ERROR_ALREADY_EXISTS。

<2> 对象所有权的释放

ReleaseMutex(HANLDE hMutex);

释放的原则是谁拥有谁释放;拥有几次,则释放几次;线程结束操作系统会自动释放

在释放互斥对象的时候,函数会拿当前线程的ID跟拥有互斥对象的ID比较,如果是同一个ID,那么就释放一次。如果ID不相等,那么就不释放。

对象在拥有一次的时候,还可以再次拥有,例如如下代码:

         hMutex = CreateMutex(NULL,TRUE,NULL);
         WaitForSingleObject(hMutex,INFINITE);
         ReleaseMutex(hMutex);
         ReleaseMutex(hMutex);

当主线程拥有Mutex的时候,该对象处于未通知状态,但是当WaitForSingleObject函数请求互斥对象的拥有权的时候,因为ID相等,所以仍然能够请求到这个互斥对象的所有权,这个时候互斥对象内部的计数器就会加1.

操作系统一旦发现线程终止但是没有释放互斥对象的时候,它就会自动的将互斥对象的线程ID设置为0,并且将其计数器归0.

4 最终实现的代码

#include <stdio.h>
#include <WinSock2.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

DWORD WINAPIfun1Proc(LPVOID lpParameter);
DWORD WINAPIfun2Proc(LPVOID lpParameter);

int index = 0;
int tickets = 100;
HANDLE hMutex;

void main()
{
         HANDLE hThread1;
         HANDLE hThread2;

         //hMutex =CreateMutex(NULL,FALSE,NULL);
         hMutex = CreateMutex(NULL,TRUE,NULL);
         WaitForSingleObject(hMutex,INFINITE);
         ReleaseMutex(hMutex);
         ReleaseMutex(hMutex);

         hThread1 =CreateThread(NULL,0,fun1Proc,NULL,0,NULL);
         hThread2 =CreateThread(NULL,0,fun2Proc,NULL,0,NULL);
         CloseHandle(hThread1);
         CloseHandle(hThread2);
         Sleep(4000);
}

DWORD WINAPIfun1Proc(LPVOID lpParameter)
{
         while(TRUE)
         {
                   WaitForSingleObject(hMutex,INFINITE);
                   if(tickets>0)
                   {
                            cout<<"Thread1 sell ticket:"<<tickets--<<endl;
                   }
                   else
                            break;
                   ReleaseMutex(hMutex);
         }
         return0;
}

DWORD WINAPIfun2Proc(LPVOID lpParameter)
{
         while(TRUE)
         {
                   WaitForSingleObject(hMutex,INFINITE);
                   if(tickets>0)
                   {
                            cout<<"Thread2 sell ticket:"<<tickets--<<endl;
                   }
                   else
                            break;
                   ReleaseMutex(hMutex);
         }
         return0;
}
时间: 2024-10-12 21:26:18

Windows 互斥对象在线程同步上的运用的相关文章

多线程程序框架-利用互斥对象实现线程同步

版权声明:本文为博主原创文章,未经博主允许不得转载.

线程同步——内核对象实现线程同步——信号量

1 /* 2 3 信号量内核对象 4 信号量与其它内核量相同,包含一个使用计数,除此之外还包含两个量. 5 一个最大资源计数和一个当前资源计数. 6 信号量规则如下: 7 如果当前资源计数大于0,那么信号量处于触发状态. 8 如果当前资源计数等于0,那么信号量处于未触发状态. 9 系统绝不会让当前资源计数变为负数. 10 当前资源计数绝不会大于最大资源计数. 11 12 下面我们看一下信号量的创建函数 13 HANDLE CreateSemaphore( 14 LPSECURITY_ATTRIB

线程同步——内核对象实现线程同步——等待函数

1 对于内核对象实现线程同步,不得不提三点: 2 1)大多数内核对象既有触发也有未触发两个状态 3 比如:进程.线程.作业.文件流.事件.可等待的计时器.信号量.互斥量 4 2)等待函数:等待函数使线程自愿进入等待状态,直到指定的内核对象变为触发状态为止, 5 说道等待我们最喜欢不过了,因为这样不会浪费我们宝贵的CPU时间. 6 3)对于自动重置对象来说,当对象被触发时,函数会自动检测到(手动重置对象为触发是,函数也能检测到), 7 并开始执行,但是在函数会在返回之前使事件变为非触发状态. 8

线程同步——内核对象实现线程同步——事件内核对象

1 事件内核对象 2 3 事件类型对象有两种不同类型,手动重置和自动重置 4 手动重置:当一个手动重置对象被触发时候,等待该对象的所有线程变为可调度. 5 自动重置:当一个自动重置对象被触发时,只有一个等待该事件的线程会变为可调度 6 7 下面是一个创建事件内核对象的函数: 8 HANDLE CreateEvent( 9 LPSECURITY_ATTRIBUTES lpEventAttributes, 10 BOOL bManualReset, 11 BOOL bInitialState, 12

线程同步——内核对象实现线程同步——可等待计时器内核对象

1 可等待计时器 2 可等待计时器是这样一种内核对象,他们会在某个指定的时间触发或每隔一段时间触发一次. 5 下面我们来介绍一下创建可等待计时器函数: 6 7 HANDLE CreateWaitableTimer( 8 LPSECURITY_ATTRIBUTES lpTimerAttributes, 9 BOOL bManualReset, 10 LPCSTR lpTimerName ); 11 第一.三个参数我想我也不用介绍了哈,这是创建内核对象基本都会有的参数. 12 第二个参数bManua

windows C++ 互相等待线程同步示例

windows C++ 互相等待线程同步示例 开发中遇到线程同步问题,可抽象为如下模型: 1.主线程的继续执行的前提是子线程的变量已经初始化完毕. 2.子线程待主线程执行完毕后继续执行后续的初始化操作. 重复1,2执行--直到通信结束. 如下图所示: 源码(VS2010编译.运行通过) #include "stdafx.h" #include <iostream> using namespace std; #include <windows.h> const c

第9章 用内核对象进行线程同步(3)_信号量(semaphore)、互斥量(mutex)

9.5 信号量内核对象(Semaphore) (1)信号量的组成 ①计数器:该内核对象被使用的次数 ②最大资源数量:标识信号量可以控制的最大资源数量(带符号的32位) ③当前资源数量:标识当前可用资源的数量(带符号的32位) (2)信号量的使用规则 ①如果当前资源计数>0,那么信号量处于触发状态,表示有可用资源. ②如果当前资源计数=0,那么信号量处于未触发状态,表示没有可用资源. ③系统绝不会让当前资源计数变为负数: ④当前资源计数绝对不会大于最大资源计数 (3)信号量的用法 (4)相关函数

第9章 用内核对象进行线程同步(4)_死锁(DeadLock)及其他

9.7 线程同步对象速查表 对象 何时处于未触发状态 何时处于触发状态 成功等待的副作用 进程 进程仍在运行的时候 进程终止的时(ExitProcess.TerminateProcess) 没有 线程 线程仍在运行的时候 线程终止的时候(ExitThread.TermimateThread) 没有 作业 作业尚未超时的时候 作业超时的时候 没有 文件 有待处理的I/O请求的时候 I/O请求完成的时候 没有 控制台输入 没有输入的时候 有输入的时候 没有 文件变更通知 文件没有变更的时候 文件系统

VB.net学习笔记(二十七)线程同步上

X夫妇二人试图同时从同一账户(总额1000)中支取1000.由于余额有1000,夫妇各自都满足条件,于是银行共支付2000.结果是银行亏了1000元.这种两个或更多线程试图在同一时刻访问同一资源来修改其状态,并产生不良后果的情况被称做竞争条件. 为避免竞争条件,需要使Withdraw()方法具有线程安全性,即在任一时刻只有一个线程可以访问到该方法. 一.线程同步 多个线程读或写同一资源,就会造成错漏状况,这时就需要线程同步.同步就是协同步调,按预定的先后次序进行运行.如:你说完,我再说.线程A与