内核对象进行线程同步

前言:

具体的可等待的内核对象有:

  进程,线程,作业,文件以及控制台的标准输入流/输出流/错误流,事件,可等待的计时器,信号量,互斥量。

等待函数:

DWORD WaitForSingleObject(
    HANDLE hObject,//用来标识要等待的内核对象
    DWORD dwMilliseconds);//等待的时间

DWORD WaitForMultipleObjects(
    DWORD dwCount,//函数检查的内核对象的数量(最大为MAXIMUM_WAIT_OBJECTS)
    CONST HANDLE* phObjects,//指向一个内核对象句柄的数组
    BOOL bWaitAll,//是否等待全部对象都触发
    DWORD dwMilliseconds);//等待时间

等待成功引起的副作用:(Windows核心编程)

当等待函数发现对象已经被触发,则称为一个成功的调用,当调用后,对象的状态发生了变化,则称之为等待成功引起的副作用。

正文:

1.事件内核对象(Event)

事件包含一个引用计数,一个用来表示事件是自动重置事件还是手动重置事件的布尔值,以及另一个用来表示事件有没有被触发的布尔值。

HANDLE CreateEvent(
    PSECURITY_ATTRIBUTES psa,
    BOOL bManualReset,//TRUE表示手工重置,FALSE表示自动重置
    BOOL bInitialState,//TRUE表示触发状态,FALSE表示未触发状态
    PCTSTR pszName);

HANDLE CreateEventEx(
    PSECURITY_ATTRIBUTES psa,
    PCTSTR pszName,
    DWORD dwFlags,//CREATE_EVENT_INITIAL_SET设置了则初始化为触发状态
            //,CREATE_EVENT_MANUAL_RESET设置了则创建一个手工重置事件
    DWORD dwDesiredAccess);//指定在创建事件时返回的句柄对事件有何种访问权限

HANDLE OpenEvent(
    DWORD dwDesiredAccess,
    BOOL bInherit,
    PCTSTR pszName);

BOOL SetEvent(HANDLE hEvent);//触发事件

BOOL ResetEvent(HANDLE hEvent);//把事件设为未触发状态

BOOL PulseEvent(HANDLE hEvent);//先触发事件后立即恢复到未触发状态

当一个手工重置事件被触发的时候,正在等待该事件的所有线程都将变成可调度状态。当一个自动重置事件被触发的时候,只有一个正在等待该事件的线程会变成可调度状态。

2.可等待的计时器内核对象(Waitable Timer)

可以在某个指定的时间触发,或每隔一段时间触发一次。

HANDLE CreateWaitableTimer(
    PSECURITY_ATTRIBUTES psa,
    BOOL bManualReset,
    PCTSTR pszName);

HANDLE OpenWaitableTimer(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    PCTSTR pszName);

BOOL SetWaitableTimer(
    HANDLE hTimer,
    const LARGE_INTEGER* pDueTime,//表示第一次触发时间在什么时候
    LONG lPeriod,//在第一次触发之后,计时器应该以怎么样的频率触发
    PTIMERAPCROUTINE pfnCompletionRoutine,//APC
    PVOID pvArgToCompletionRoutine,//APC参数
    BOOL bResume);

BOOL CancelWaitableTimer(HANDLE hTimer);//取消计时器

设置可等待计时器时候的pDueTime参数是LARGE_INTEGER*类型的,需要将SYSTEMTIME类型的时间利用SystemTimeToFileTime()转换位FileTime,再将FileTime的成员复制到LARGE_INTEGER结构成员中,然后再将LARGE_INTEGER的地址传给pDueTime。

3.信号量内核对象(Semaphore)

包含一个使用计数,还有一个最大资源计数,一个当前资源计数。

信号量的使用规则如下:

1如果当前的资源计数大于0,那么信号量处于触发状态;

2如果当前的资源计数等于0,那么信号量处于未触发状态;

3系统绝对不会让当前资源计数变为负数

4当前资源计数绝对不会大于最大资源计数

HANDLE CreateSemaphore(
    PSECURITY_ATTRIBUTE psa,
    LONG lInitialCount,
    LONG lMaximumCount,
    PCTSTR pszName);

HANDLE CreateSemaphoreEx(
    PSECURITY_ATTRIBUTES psa,
    LONG lInitialCount,
    LONG lMaximumCount,
    PCTSTR pszName,
    DWORD dwFlags,//设为0,系统保留
    DWORD dwDesiredAccess);

BOOL ReleaseSemaphore(  HANDLE hSemaphore,  LONG lReleaseCount,  PLONG plPreviousCount);//递增信号量的当前资源计数

信号量以原子方式来执行测试和设置操作,当我们向信号量请求一个资源的时候,操作系统会检查资源是否可用,并将可用资源的数量递减,整个过程不会被别的线程打断。

4.互斥量内核对象(Mutex)

互斥量对象包含一个使用计数、线程ID以及一个递归计数。线程ID用来标识当前占用这个互斥量的是系统中哪个线程,递归计数表示这个线程占用该互斥量的次数。

互斥量的使用规则:

1如果线程ID为0(无效),那么该互斥量不为任何线程所占用,他处于触发状态。

2如果线程ID为非0值,那么有一个线程已经占用了该互斥量,他处于未触发状态

3与所有其他内核对象不同,操作系统对互斥量进行了特殊处理,允许它们违反一些常规的规则(下面说)

HANDLE CreateMutex(
    PSECURITY_ATTRIBUTES pas,
    BOOL bInitialOwner,//控制互斥量的初始状态,如果传FALSE,              //那么互斥量对象的线程ID和递归计数都将被设为0(触发状态),              //传TRUE,线程ID为调用线程的线程ID,递归计数将被设为1(未触发状态)
    PCTSTR pszName);
HANDLE CreateMutexEx(
    PSECURITY_ATTRIBUTES psa,
    PCTSTR pszName,
    DWORD dwFlags,
    DWORD dwDesiredAccess);

HANDLE OpenMutex(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    PCTSTR pszName);

BOOL ReleaseMutex(HANDLE hMutex);//释放互斥量,递归计数减1

使用方法:

为了获得对被保护资源的访问权,线程要调用一个等待函数并传入互斥量的句柄。在内部,等待函数会检查线程ID是否为0.如果为0,那么函数会把线程ID设为调用线程的线程ID,把递归计数设为1,然后让调用线程继续运行。(相当于关键段的EnterCriticalSection())。

如果等待函数检测到线程ID不为0,那么调用线程将进入等待状态。当另一个线程将互斥量的线程ID设为0的时候,系统会记得有一个线程正在等待,于是它把线程ID设为正在等待的那个线程的线程ID,把递归计数设为1,使正在等待的线程变为可调度状态。

上面所说的互斥量的一切都与其他的内核对象一样,但是互斥量有一些自己的特性:

上面说的违反一些常规规则便是:如果等待函数检测到线程ID不为0但是和目前的调用线程的线程ID相等,那么系统会让线程保持可调度状态——即使该互斥量尚未触发,然后递归计数加1,使递归计数大于1的唯一途径是利用这个例外,让线程多次等待同一个互斥量。

当释放互斥量时ReleaseMutex也会检查调用线程的线程ID与互斥量内部的线程ID是否一致,如果一致,递归计数会递减;如果不一致,那么将返回FALSE,调用GetLastError会返回ERROR_NOT_OWNER。

更多的等待函数:

//等待hProcess标识的进程,直到创建应用程序第一个窗口的线程没有待处理的输入为止。
DWORD WaitForInputIdle(
    HANDLE hProcess,
    DWORD dwMilliseconds);

//不仅内核对象被触发的是时候调用线程会变成可调度状态,而且当窗口信息需要被派送到一个
//由调用线程创建的窗口时,他们也会变成可调度状态。
DWORD MsgWaitForMultipleObjects(
    DWORD dwCount,
    PHANDLE phObjects,
    BOOL bWaitAll,
    DWORD dwMilliseconds,
    DWORD dwWakeMask);

DWORD MsgWaitForMultipleObjectsEx(
    DWORD dwCount,
    PHANDLE phObjects,
    DWORD dwMilliseconds,
    DWORD dwWakeMask,
    DWORD dwFlags);

BOOL WaitForDebugEvent(
    PDEBUG_EVENT pde,
    DWORD dwMilliseconds);

//原子操作来触发一个内核对象并等待另一个内核对象
DWORD SignalObjectAndWait(
    HANDLE hObjectToSignal,//只能是互斥量,事件,信号量内核对象
    HANDLE hObjectToWaitOn,//可等待的内核对象即可
    DWORD dwMilliseconds,
    BOOL bAlertable);

原文地址:https://www.cnblogs.com/Anony-WhiteLearner/p/8625013.html

时间: 2024-10-08 10:08:51

内核对象进行线程同步的相关文章

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

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

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

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

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

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

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

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

第9章 用内核对象进行线程同步(2)_可等待计时器(WaitableTimer)

9.4 可等待的计时器内核对象——某个指定的时间或每隔一段时间触发一次 (1)创建可等待计时器:CreateWaitableTimer(使用时应把常量_WIN32_WINNT定义为0x0400) 参数 描述 psa 安全属性(如使用计数.句柄继承等) bManualReset 手动重置计时器还是自动重置计时器. ①当手动计时器被触发,所有正在等待计时器的线程都变可为可调度. ②当自动计时器被触发时,只有一个正在等待计数器的线程变为可调度 pszName 对象的名字 (2)也可以打开一个己经存在的

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

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

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

互斥对象在线程同步时的使用 1 多线程在资源共享的时候出现的问题 在程序中如果不同线程对同一个对象进行操作的话就有可能出现因为线程切换而导致的问题.例如下面的程序 #include <stdio.h> #include <WinSock2.h> #include <iostream> using namespace std; #pragma comment(lib,"ws2_32.lib") DWORD WINAPIfun1Proc(LPVOID l

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

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