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

9.7 线程同步对象速查表


对象


何时处于未触发状态


何时处于触发状态


成功等待的副作用


进程


进程仍在运行的时候


进程终止的时(ExitProcess、TerminateProcess)


没有


线程


线程仍在运行的时候


线程终止的时候(ExitThread、TermimateThread)


没有


作业


作业尚未超时的时候


作业超时的时候


没有


文件


有待处理的I/O请求的时候


I/O请求完成的时候


没有


控制台输入


没有输入的时候


有输入的时候


没有


文件变更通知


文件没有变更的时候


文件系统检测到变更的时候


重置通知


自动重置事件


ResetEvent、PulseEvent或等待成功的时候


SetEvent/PulseEvent被调用的时候


重置事件


手动重置事件


ResetEvent、PulseEvent


SetEvent/PulseEvent被调用的时候


没有


自动重置可等待计时器


CancelWaitableTimer或等待成功的时候


时间到的时候(SetWaitableTimer)


重置计时器


手动重置可等待计时器


CancelWaitableTimer


时间到的时候(SetWaitableTimer)


没有


信号量


等待成功的时候


计数大于0的时候(ReleaseSemaphore)


计数减1


互斥量


等待成功的时候


不为线程占用的时候(ReleaseMutex)


把所有权交给线程


关键段

(用户模式)


等待成功的时候

(Try)EnterCriticalSection


不为线程占用的时候

(LeaveCriticalSection)


把所有权交给线程


SRWLock

(用户模式)


等待成功的时候

(AcuquireSRWLock(Exclusive))


不为线程占用的时候

(ReleaseSRWLock(Exclusive))


把所有权交给线程


条件变量

(用户模式)


等待成功的时候

(SleepConditionVaiable*)


被唤醒的时候

(Wake(All)ConditionVariable)


没有


InterLocked*

(用户模式)


从来不会使线程变成不可调度状态,它只是修改一个值并立即返回

9.8 其他的线程同步函数

9.8.1 WaitForInputIdle(hProcess,dwMilliseconds)函数

(1)等待进程,直到创建第一个窗口的线程处于输入“空闲”状态时(我的理解是这个线程消息队列中没有键盘和鼠标的消息了,这理解可能不准确!)。当父进程创建子进程时,父进程可以一边继续执行,一边让子进程初始化。这是父进程能够知道子进程己经初始化完毕的唯一方法,就是等待子进程,直到它不再处理任何输入为止。可以调用CreateProcess后,调用WaitForInputIdle。

(2)当要在程序中用模拟用发送键盘消息的方式来打开一个对话框里,也可以用WaitForInputIdle来等待这个对话框。如模拟“Alt+F,O”来打开“打开文件对话框”,可依次发送WM_KEYDOWN(VK_MENU)、WM_KEYDOWN(VK_F)、WM_KEYUP(VK_MENU)、WM_KEYUP(VK_F)、WM_KEYDOWN(VK_O)、WM_KEYUP(VK_O)

,此时系统会创建这个对话框,但由于对话框上可能还要多个子控件,这创建需要一定的时间,所以可以在发送键盘消息后,调用WaitForInputIdle来等待对话框创建完毕。

【WaitForInputIdle程序】模拟发送“Alt+F,0”组合键来打开“打开文件对话框”

#include <windows.h>
#include <process.h>
#include <locale.h>
#include <tchar.h>

//模拟按“ALT+F,O”组合键打开“Open Dialog”对话框
int _tmain()
{
    _tsetlocale(LC_ALL, _T("chs"));

    //根据标题获o取窗体的句柄
    HWND hwnd = FindWindow(_T("NotePad"), NULL);
    if (hwnd){

        //通过窗体句柄获取记事本进程ID
        DWORD dwProcessID;
        GetWindowThreadProcessId(hwnd, &dwProcessID);

        //将进程ID转为进程的句柄
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID);

        SetForegroundWindow(hwnd); //这句一定要加上去

        //第1种方法
        //keybd_event(VK_LMENU, 0, 0, 0); // 按 Alt
        //keybd_event(‘F‘, 0, 0, 0);
        //keybd_event(‘F‘, 0, KEYEVENTF_KEYUP, 0); // F弹上
        //keybd_event(VK_LMENU, 0, KEYEVENTF_KEYUP, 0); // Alt弹上
        //keybd_event(‘O‘, 0, 0, 0);   //"O"键按下
        //keybd_event(‘O‘, 0, KEYEVENTF_KEYUP, 0); // "O"键弹上

        //第2种方法
        INPUT input[6];
        memset(input, 0, sizeof(input));
        for (int i = 0; i < 6; i++){
            input[i].type = INPUT_KEYBOARD;
        }

        //按键顺序
        input[0].ki.wVk = input[3].ki.wVk = VK_LMENU;
        input[1].ki.wVk = input[2].ki.wVk = ‘F‘;
        input[4].ki.wVk = input[5].ki.wVk = ‘O‘;

        //按键状态(按下或释放)
        input[2].ki.dwFlags = input[3].ki.dwFlags = input[5].ki.dwFlags = KEYEVENTF_KEYUP;

        SendInput(6, input, sizeof(INPUT));

        if (hProcess){
            WaitForInputIdle(hProcess, INFINITE);//等待对话框创建完成
            _tprintf(_T("打开对话框%s!\n"), (GetLastError() == 0) ? _T("成功") : _T("失败"));
            CloseHandle(hProcess);
        }
    }

    _tsystem(_T("PAUSE"));
    return 0;
}

9.8.2 MsgWaitForMultipleObjects(Ex)函数

(1)当内核对象被触发或当窗口消息被派送到一个由调用线程创建的窗口时都会将调用线程变为可调度状态。即该函数不仅可以等待对象触发,也可以等待指定的消息时触发。

(2)WaitForMultipleObjects会阻塞调用线程,因此以下两种线程:①创建窗口的线程执行与用户界面相关的任务的线程,不应使用该函数。因为当线程被挂起时,用户在用户界面上的操作将无法得到响应。这时可使用MsgWaitForMultipleObjectEx来替代。

【MsgWaitForMultipleObjects的一般用法】

BOOL bLoop = TRUE;
MSG msg;

while (bLoop)
{
    DWORD dwRet = MsgWaitForMultipleObjects(1, &hEventOk, FALSE, 10, QS_ALLINPUT);
    switch (dwRet)
    {
    case WAIT_OBJECT_0: //等待对象己触发
        ...... //进行相应处理        bLoop = FALSE; //跳出循环
        break;

    case WAIT_OBJECT_0 + 1: //界面消处到达
        //从消息队列中获取消息并分派到指定的窗口
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        break;

    case WAIT_TIMEOUT: //超时处理
        break;
    }
}
时间: 2024-11-05 16:39:57

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

第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)相关函数

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

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

内核对象进行线程同步

前言: 具体的可等待的内核对象有: 进程,线程,作业,文件以及控制台的标准输入流/输出流/错误流,事件,可等待的计时器,信号量,互斥量. 等待函数: DWORD WaitForSingleObject( HANDLE hObject,//用来标识要等待的内核对象 DWORD dwMilliseconds);//等待的时间 DWORD WaitForMultipleObjects( DWORD dwCount,//函数检查的内核对象的数量(最大为MAXIMUM_WAIT_OBJECTS) CONS

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

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

线程同步锁、死锁、递归锁、信号量、GIL

目录 线程同步锁.死锁.递归锁.信号量.GIL 一.同步锁 二.死锁 三.递归锁(Rlock) 四.信号量(Semphare) 五.GIL(全局解释器锁) io密集型 计算密集型 线程同步锁.死锁.递归锁.信号量.GIL 一.同步锁 所有线程同一时间读写同一个数据,有的线程已经对数据进行修改了,造成有的线程拿到的数据时旧的数据,而不是修改后的数据,造成结果不正确,于是引入了同步锁解决问题, 同步锁的原理是同一时间只能有一个线程读写数据. 锁通常被用来实现对共享资源的同步访问.从threading