Windows内核模式下的线程同步

Windwos下内核模式/用户模式实现线程同步的比较
  • 在用户模式下进行线程同步的最大好处就是非常快。利用内核对象进行线程同步时,调用线程必须从用户模式切换到内核模式,此过程相对挺慢的
  • 需要在多个进程间进行线程同步时或者线程同步时希望设置超时时间,那么只能利用内核对象进行线程同步

常用等待函数
函数 功能
WaitForSingleObject Waits until the specified object is in the signaled state or the time-out interval elapses.
WaitForMultipleObjects Waits until one or all of the specified objects are in the signaled state or the time-out interval elapses.

参考资料 https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject

事件内核对象
函数 功能
CreateEvent Creates or opens a named or unnamed event object
第二个参数为TRUE代表手动,为FALSE代表自动
第三个参数代表初始状态是否为触发状态
OpenEvent Opens an existing named event object.
SetEvent Sets the specified event object to the signaled state.
ResetEvent Sets the specified event object to the nonsignaled state.
情况 手动重置事件 自动重置事件
事件内核对象被触发 所有等待该事件的线程均变为可调度状态 只有一个等待该事件的线程变为可调度状态
等待事件内核对象成功的副作用 自动置为非触发状态


可等待的计时器内核对象
函数 功能
CreateWaitableTimer Creates or opens a waitable timer object.
第二个参数:TRUE表示手动重置,为FALSE表示为自动重置
OpenWaitableTimer Opens an existing named waitable timer object.
SetWaitableTimer Activates the specified waitable timer. When the due time arrives, the timer is signaled and the thread that set the timer calls the optional completion routine.
CancelWaitableTimer Sets the specified waitable timer to the inactive state.
情况 手动重置计时器 自动重置计时器
计时器内核对象被触发 所有等待该计时器内核对象的线程均变为可调度状态 只有一个等待该计时器内核对象的线程变为可调度状态
等待的副作用 自动置为非触发状态
SetWaitableTimer详解
参数 解释
HANDLE hTimer 可等待的计时器内核对象句柄
const LARGE_INTEGER *lpDueTime 第一次触发时间,可以是一个绝对时间(此时间可以已逝去),可以是一个相对调用时间(传负值,传入值必须是100纳秒的整数倍)
LONG lPeriod 间隔触发时间,单位为毫秒,传0代表只触发一次
PTIMERAPCROUTINE pfnCompletionRoutine 回调函数
LPVOID lpArgToCompletionRoutine 回调函数参数
BOOL fResume 一般为 FALSE
注意点
  • 可等待的计时器内核对象会在某一指定的时间触发,或每隔一段时间触发一次
  • 当且仅当SetWaitableTimer的调用线程处于可提醒状态时,由此函数设定的回调函数会被同一线程调用(异步过程调用,APC)。
  • 只有当所有的APC函数都处理完毕后,才会返回主调线程
  • 有关APC的调用过程,参考资料: https://docs.microsoft.com/en-us/windows/desktop/Sync/asynchronous-procedure-calls
  • 一般在使用计时器内核对象时,很少要用到APC,直接等待计时器内核对象被触发,然后做想做的事情就好了
使用示例
#include 
#include 

int main()
{
    HANDLE hWaitableTime = CreateWaitableTimer(nullptr, FALSE, nullptr);
 
    LARGE_INTEGER nLargeValue = {};
    SYSTEMTIME SystemTime = {};
    FILETIME aFileTime[2] = {};
    SystemTime.wYear = 2018;
    SystemTime.wMonth = 2;
    SystemTime.wDay = 6;
    SystemTime.wHour = 20;
    SystemTime.wMinute = 41;
    SystemTime.wSecond = 0;
    SystemTime.wMilliseconds = 0;
    SystemTimeToFileTime(&SystemTime, aFileTime);
    LocalFileTimeToFileTime(aFileTime, aFileTime + 1);
    nLargeValue.HighPart = aFileTime[1].dwHighDateTime;
    nLargeValue.LowPart = aFileTime[1].dwLowDateTime;
    //以上指定一个绝对时间
 
    SetWaitableTimer(hWaitableTime, &nLargeValue, 1000, 
        nullptr, nullptr, FALSE);
 
    while(true)
    {
        WaitForSingleObject(hWaitableTime, INFINITE);
        printf("Hello World\n");
    }
 
    CloseHandle(hWaitableTime);
    return 0;
    //程序每隔1s输出Hello World
}
#include 
#include 

void __stdcall FunTest(void* pIn, unsigned long dwTimerLowValue, unsigned long dwTimerHighValue)
{
    printf("CallBack:%d\n", ++*static_cast(pIn));
}


int main()
{
    HANDLE hWaitableTime = CreateWaitableTimer(nullptr, FALSE, nullptr);

    LARGE_INTEGER nLargeValue = {};
    nLargeValue.QuadPart = -1 * 10000000;

    int nValue = 0;
    SetWaitableTimer(hWaitableTime, &nLargeValue, 1000, FunTest, &nValue, FALSE);
    //会在此函数调用后 每隔1s调用 FunTest

    while (true)
    {
        SleepEx(1, TRUE);   //将线程置为可提醒状态
    }
    CloseHandle(hWaitableTime);

    return 0;
}


信号量内核对象
函数 功能
CreateSemaphore Creates or opens a named or unnamed semaphore object.
OpenSemaphore Opens an existing named semaphore object.
ReleaseSemaphore Increases the count of the specified semaphore object by a specified amount.
信号量的规则
  • 信号量内核对象包含一个最大资源计数和一个当前资源计数,前者代表信号量可以控制的最大资源数量,后者表示信号量当前可用资源的数量
  • 如果当前资源计数大于0,那么信号量处于触发状态
  • 如果当前资源计数等于0,那么信号量处于未触发状态
  • 当前资源计数不会变为负数且不会大于最大资源计数
  • 等待成功的副作用:当前资源计数减一


互斥量内核对象
函数 功能
CreateMutex Creates or opens a named or unnamed mutex object.
OpenMutex Opens an existing named mutex object.
ReleaseMutex Releases ownership of the specified mutex object.
互斥量内核对象的规则
  • 互斥量内核对象用来确保一个线程独占对一个资源的访问
  • 互斥量内核对象包含一个使用计数、线程ID以及一个递归计数
  • 如果互斥量内核对象的线程ID为0(无效线程ID),那么该互斥量不为任何线程所占有,处于触发状态
  • 如果互斥量内核对象的线程ID不为0,那么有一个线程已经占用了该互斥量内核对象,处于未触发状态
  • 互斥量内核对象具有线程所有权的概念,如果占用互斥量内核对象的线程在释放互斥量内核对象前终止,那么该互斥量会被当做遗弃,并且该互斥量内核对象会变为触发状态,其他线程等待此互斥量内核对象会返回WAIT_ABANDONED
  • The state of a mutex object is signaled when it is not owned by any thread. The creating thread can use the bInitialOwner flag to request immediate ownership of the mutex. Otherwise, a thread must use one of the wait functions to request ownership. When the mutex's state is signaled, one waiting thread is granted ownership, the mutex's state changes to nonsignaled, and the wait function returns. Only one thread can own a mutex at any given time. The owning thread uses the ReleaseMutex function to release its ownership.
    The thread that owns a mutex can specify the same mutex in repeated wait function calls without blocking its execution. Typically, you would not wait repeatedly for the same mutex, but this mechanism prevents a thread from deadlocking itself while waiting for a mutex that it already owns. However, to release its ownership, the thread must call ReleaseMutex once for each time that the mutex satisfied a wait.
    参考资料 https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createmutexa

你可能感兴趣的:(Windows内核模式下的线程同步)