7.1 线程的挂起和恢复
(1)线程挂起
①创建时(如CreateProcess、CreateThread),传入CREATE_SUSPENDED标志
②用SuspendThread挂起线程。这个函数可以挂起自己,也可以挂起其它线程(只要有线程句柄)
③线程在挂机计数不为0或没有消息队列没有消息时,是不可调度的(没时间片)
(2)线程恢复:ResumeThread,返回前一个挂起计数,否则返回0xFFFFFFFF。
①一个线程可以被多次挂起,最多可以挂起MAXIMUNM_SUSPEND_COUNT(127)次
②线程被挂起多次时,只有恢复到挂起计数为0时,才可以重新被调度。
③调用SuspendThread时,如果这时线程执行在用户态,线程会马上被挂起。如果调用SuspendThread时线程己经执行在内核态时,SuspendThread会异步返回,而线程并不会马上暂停。但当该线程从内核态又转为用户态时,则会立即被暂停。
④当调用SuspendThread挂起线程时,我们并不知道线程在做什么?如果此时A线程正在分配堆中的内存,则它将会锁定堆,这会导致此时也要访问堆的B线程被中止,直到A恢复,而这可能引起其他问题或者死锁。所以只有在确切目标线程在哪里(或在做什么时)调用SuspendThread才是安全的。
(3)可以调用GetSystemTimeAdjustment来查看线程切换的周期(大约20ms)
7.2 进程的挂起和恢复
(1)进程的挂起——挂起进程中所有的线程(注意进程本身是不可调度的)
①Windows没有提供挂起进程中所有线程的方法,因为存在竞态条件问题。(如在线程被挂起时,可能创建一个新的线程。系统必须想方设法挂起这个时间段内创建任何新线程。
②自定义的SuspendProcess函数(用来挂起所有线程)
【SuspendProcess程序】
#include <windows.h> #include <stdio.h> #include <Tlhelp32.h> #include <tchar.h> #include <locale.h> //挂起进程中所有的线程 void SuspendProcess(DWORD dwProcessID, BOOL fSuspend) { DWORD dwThreadID = GetCurrentThreadId(); //主调线程ID //获取系统中所有线程列表,第2个参数为0时表示当前进程 HANDLE hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, dwProcessID); if (hSnapshot != INVALID_HANDLE_VALUE){ /* 以下在枚举线程过程中,可能有新的线程创建,也可能有线程被销毁。但CreateToolhelp32SnapShot只是快照,无法反应这一变化。 所以新的线程就不会被挂机。同时,被销毁的线程ID可能被另一个进程中的线程给占用,这会造成误挂其他进程中的线程的潜在风险。因此这个函数要慎用。 */ //遍历线程列表 THREADENTRY32 te = { sizeof(te) }; BOOL fOk = Thread32First(hSnapshot, &te); for (; fOk;fOk=Thread32Next(hSnapshot,&te)){ //线程是否在目标进程中 if (te.th32OwnerProcessID != dwProcessID) continue;; //尝试将线程ID转换为线程句柄(可挂机和恢复线程) HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID); if (hThread != NULL){ //挂机或恢复线程 if (fSuspend) { //防止主调函数挂起自己,导致循环无法进行而不能挂机其他线程 if (te.th32ThreadID != dwThreadID) SuspendThread(hThread); } else ResumeThread(hThread); } CloseHandle(hThread); } CloseHandle(hSnapshot); //如果要挂机进程,最后挂起主调进程 if (fSuspend){ HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, dwThreadID); if(hThread) SuspendThread(hThread); CloseHandle(hThread); } } } //线程函数 DWORD WINAPI ThreadProc(PVOID pParam) { while (TRUE){ _tprintf(_T("线程(ID:0x%04X),正在输出... \t时间%d\n"), GetCurrentThreadId(), GetTickCount()); Sleep(1000); } return 0; } int _tmain() { _tsetlocale(LC_ALL, _T("chs")); //创建两个线程,线程刚创建时是挂起的——这两个线程用来测试SuspendProcess函数 HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc, NULL, CREATE_SUSPENDED, NULL); HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc, NULL, CREATE_SUSPENDED, NULL); CloseHandle(hThread1); CloseHandle(hThread2); //唤醒所有线程 _tprintf(_T("正在唤醒线程...\n")); SuspendProcess(GetCurrentProcessId(), FALSE); _tsystem(_T("PAUSE")); //在线程运行过程, 按任意键可以挂起进程 _tprintf(_T("进程己被挂起!\n")); SuspendProcess(GetCurrentProcessId(), TRUE); //因所有线程都被挂起,进程中无活动线程,故进程(线)程无法被唤醒 //所以后面的代码无法执行! //再次唤醒所有线程 _tprintf(_T("正在唤醒线程...\n")); SuspendProcess(GetCurrentProcessId(), FALSE); _tsystem(_T("PAUSE")); return 0; }