• 内核对象进行线程同步


    前言:

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

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

    等待函数:

    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);
  • 相关阅读:
    5.win上安装ES
    6.入门案例:电商网站商品管理(一)
    BZOJ 1616 [Usaco2008 Mar]Cow Travelling游荡的奶牛:dp【网格型】
    BZOJ 1626 [Usaco2007 Dec]Building Roads 修建道路:kruskal(最小生成树)
    BZOJ 1614 [Usaco2007 Jan]Telephone Lines架设电话线:spfa + 二分【路径中最大边长最小】
    BZOJ 1612 [Usaco2008 Jan]Cow Contest奶牛的比赛:floyd传递闭包
    BZOJ 1609 [Usaco2008 Feb]Eating Together麻烦的聚餐:LIS & LDS (nlogn)
    POJ 2976 Dropping tests:01分数规划【二分】
    BZOJ 1607 [Usaco2008 Dec]Patting Heads 轻拍牛头:统计 + 筛法【调和级数】
    BZOJ 1605 [Usaco2008 Open]Crisis on the Farm 牧场危机:dp【找转移路径】
  • 原文地址:https://www.cnblogs.com/Anony-WhiteLearner/p/8625013.html
Copyright © 2020-2023  润新知