Windows
线程的创建和退出
概念区分
程序:磁盘上的可执行文件(如.exe)
进程:程序执行代码所需资源的集合,不活泼的(状态)
线程:程序执行代码的最小单位,活泼的(状态)
进程为代码执行提供各种各样的资源 本身并不执行代码,由线程进行代码的执行
线程的运行(调度)原理
CPU时间切片:系统将 CPU 的运行时间划分为固定长度的时间片,每个时间片结束时,当前运行的线程会被暂停,然后调度器会选择下一个就绪的线程继续执行
基于CPU时间片方式进行线程调度,就是当只有线程得到CPU的时间片才能执行指令,当线程处于执行的状态,但是却没有分配到时间片,那么就会处于就绪状态,等待系统分配下一个时间片。
保存环境和恢复环境:当线程被切走时,系统会把当前线程的环境保存,当线程再切回来时,恢复环境。
多线程效率与cpu的内核数量和逻辑处理器数量有关 当线程数与逻辑处理器的数量相同时效率最佳 并非线程越多效率越高

线程的创建
线程:
UI线程(主线程):单线程的执行环境,所有的UI控件都必须在主线程上创建和更新,否则会引发跨线程访问的异常(不运行耗时长的任务)
非UI线程(工作线程):处理一些非UI相关的任务,需要自己负责管理线程的生命周期和同步机制,以避免线程间的竞争和死锁(运行耗时长的任务
)
创建一个计数器

将.cpp中用不到的代码删除
保留的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
#include "framework.h" #include "Counter.h"
#define MAX_LOADSTRING 100
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { return (int)DialogBox(hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), NULL, About);; }
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE;
case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; }
|

创建线程

参数2:分配的栈的大小(给0 为默认的栈大小)
默认栈大小:

线程回调

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) { case BTN_START: { HANDLE hThread = CreateThread(NULL, 0, ThreadProc, hDlg, 0,NULL); CloseHandle(hThread); break; } case BTN_PAUSE: g_bContinue = FALSE; break; case BTN_CONTINUE: g_bContinue = TRUE; break; case BTN_STOP: break; default: break; } break; case WM_CLOSE: EndDialog(hDlg, 0);; break; } return (INT_PTR)FALSE; }
|
回调函数:
1 2 3 4 5 6 7 8 9 10 11 12
| DWORD g_dwCounter = 0; BOOL g_bContinue = TRUE; DWORD WINAPI ThreadProc(LPVOID lpParameter) { HWND hDlg = (HWND)lpParameter; while (true) { if (g_bContinue) { ++g_dwCounter; SetDlgItemInt(hDlg, EDT_COUNTER, g_dwCounter, FALSE); } } } }
|
由于暂停时while仍然在执行 使得线程不会停止 cpu的占用不变 用到sleepAPI使得暂停时线程的占用减小

1 2 3 4 5 6 7 8 9 10 11 12 13
| DWORD WINAPI ThreadProc(LPVOID lpParameter) { HWND hDlg = (HWND)lpParameter; while (true) { if (g_bContinue) { ++g_dwCounter; SetDlgItemInt(hDlg, EDT_COUNTER, g_dwCounter, FALSE); } else { Sleep(1); } } }
|
未暂停:

暂停时:

暂停后counter.exe占用的cpu就大大减少了
线程挂起:不再分配时间切片

恢复:

挂起几次对应的就得恢复几次 线程无法恢复自己(可以挂起自己)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| DWORD g_dwCounter = 0; BOOL g_bContinue = TRUE; HANDLE g_hThread = NULL;
DWORD WINAPI ThreadProc(LPVOID lpParameter) { HWND hDlg = (HWND)lpParameter; while (true) { ++g_dwCounter; SetDlgItemInt(hDlg, EDT_COUNTER, g_dwCounter, FALSE);
} }
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) { case BTN_START: { g_hThread = CreateThread(NULL, 0, ThreadProc, hDlg, 0,NULL); break; } case BTN_PAUSE: SuspendThread(g_hThread); break; case BTN_CONTINUE: ResumeThread(g_hThread); break; case BTN_STOP: break; default: break; } break; case WM_CLOSE: EndDialog(hDlg, 0);; break; } return (INT_PTR)FALSE; }
|
运行结果

线程的退出
退出线程:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
|
#include "framework.h" #include "Counter.h"
#define MAX_LOADSTRING 100
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { return (int)DialogBox(hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), NULL, About);; }
DWORD g_dwCounter = 0; BOOL g_bContinue = TRUE; HANDLE g_hThread = NULL; BOOL g_bStop = TRUE;
class CFoo { public: CFoo() { MessageBox(NULL, "CFoo()", NULL, MB_OK); }; ~CFoo() { MessageBox(NULL, "~CFoo()", NULL, MB_OK); }; };
DWORD WINAPI ThreadProc(LPVOID lpParameter) { CFoo foo; HWND hDlg = (HWND)lpParameter; while (TRUE) { if (g_bStop) { ExitThread(0x1234); } ++g_dwCounter; SetDlgItemInt(hDlg, EDT_COUNTER, g_dwCounter, FALSE);
#if 0 if (g_bContinue) { ++g_dwCounter; SetDlgItemInt(hDlg, EDT_COUNTER, g_dwCounter, FALSE); } else { Sleep(1); } #endif
} return 0; }
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) { case BTN_START: { g_bStop = FALSE; g_hThread = CreateThread(NULL, 0, ThreadProc, hDlg, 0,NULL); break; } case BTN_PAUSE: SuspendThread(g_hThread); break; case BTN_CONTINUE: ResumeThread(g_hThread); break; case BTN_STOP: CloseHandle(g_hThread); g_bStop = TRUE; break; default: break; } break; case WM_CLOSE: EndDialog(hDlg, 0);; break; } return (INT_PTR)FALSE; }
|
点击开始后会出现类的构造函数

而点击停止后没有析构函数 说明资源没有释放

终止线程:

使用TerminateThread不会释放关键节、堆锁
正常终止线程:
1.线程使用的堆栈等被释放(线程之间有独立的堆栈互不影响)
2.系统将线程对象的退出代码设置为线程的退出码
3.线程内核对象使用计数递减1
4.线程内核对象状态变为已通知
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
|
#include "framework.h" #include "Counter.h"
#define MAX_LOADSTRING 100
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { return (int)DialogBox(hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), NULL, About);; }
DWORD g_dwCounter = 0; BOOL g_bContinue = TRUE; HANDLE g_hThread = NULL; BOOL g_bStop = TRUE;
class CFoo { public: CFoo() { MessageBox(NULL, "CFoo()", NULL, MB_OK); }; ~CFoo() { MessageBox(NULL, "~CFoo()", NULL, MB_OK); }; };
DWORD WINAPI ThreadProc(LPVOID lpParameter) { CFoo foo; HWND hDlg = (HWND)lpParameter; while (TRUE) { if (g_bStop) { ExitThread(0x1234); } ++g_dwCounter; SetDlgItemInt(hDlg, EDT_COUNTER, g_dwCounter, FALSE); } return 0; }
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) { case BTN_START: { g_bStop = FALSE; g_hThread = CreateThread(NULL, 0, ThreadProc, hDlg, 0,NULL); break; } case BTN_PAUSE: SuspendThread(g_hThread); break; case BTN_CONTINUE: ResumeThread(g_hThread); break; case BTN_STOP: TerminateThread(g_hThread, 0x123456); CloseHandle(g_hThread); break; default: break; } break; case WM_CLOSE: EndDialog(hDlg, 0);; break; } return (INT_PTR)FALSE; }
|
面对dll时的情况



点击开始时会显示DLL_THREAD_ATTACH
点击停止时会显示DLL_THREAD_DETACH
终止线程方法
1.线程函数返回(最佳方法)
2.调用ExitThread函数,线程自行撤销(不推荐)
——操作系统清楚该线程使用的所有操作系统资源,但是C++资源(如C++类对象)将不被撤销
3.调用TerminateThread(避免使用)
——拥有线程的进程终止运行之前,系统不撤销线程堆栈
——DLL接不到通知
4.调用TerminateProcess(避免使用)
counter.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
|
#include "framework.h" #include "Counter.h"
#define MAX_LOADSTRING 100 __declspec(dllexport) void Foo(); #pragma comment(lib,"./x64/Debug/Dll1.lib") INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { Foo(); return (int)DialogBox(hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), NULL, About);; }
DWORD g_dwCounter = 0; BOOL g_bContinue = TRUE; HANDLE g_hThread = NULL; BOOL g_bStop = TRUE;
class CFoo { public: CFoo() { MessageBox(NULL, "CFoo()", NULL, MB_OK); }; ~CFoo() { MessageBox(NULL, "~CFoo()", NULL, MB_OK); }; };
DWORD WINAPI ThreadProc(LPVOID lpParameter) { CFoo foo; HWND hDlg = (HWND)lpParameter; while (TRUE) { if (g_bStop) { ExitThread(0x1234); } ++g_dwCounter; SetDlgItemInt(hDlg, EDT_COUNTER, g_dwCounter, FALSE);
#if 0 if (g_bContinue) { ++g_dwCounter; SetDlgItemInt(hDlg, EDT_COUNTER, g_dwCounter, FALSE); } else { Sleep(1); } #endif
} return 0; }
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) { case BTN_START: { g_bStop = FALSE; g_hThread = CreateThread(NULL, 0, ThreadProc, hDlg, 0,NULL); break; } case BTN_PAUSE: SuspendThread(g_hThread); break; case BTN_CONTINUE: ResumeThread(g_hThread); break; case BTN_STOP: CloseHandle(g_hThread); g_bStop = TRUE; break; default: break; } break; case WM_CLOSE: ExitThread(0x12345678); EndDialog(hDlg, 0); break; } return (INT_PTR)FALSE; }
|
dllmain.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include "pch.h"
__declspec(dllexport) void Foo() { MessageBox(NULL, "Foo Called",NULL,MB_OK); }
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: OutputDebugString("Dll1 DLL_PROCESS_ATTACH"); break; case DLL_THREAD_ATTACH: OutputDebugString("Dll1 DLL_THREAD_ATTACH"); break; case DLL_THREAD_DETACH: OutputDebugString("Dll1 DLL_THREAD_DETACH"); break; case DLL_PROCESS_DETACH: OutputDebugString("Dll1 DLL_PROCESS_DETACH"); break; } return TRUE; }
|