Windows

线程的创建和退出

概念区分

程序:磁盘上的可执行文件(如.exe)

进程:程序执行代码所需资源的集合,不活泼的(状态)

线程:程序执行代码的最小单位,活泼的(状态)

进程为代码执行提供各种各样的资源 本身并不执行代码,由线程进行代码的执行

线程的运行(调度)原理

CPU时间切片:系统将 CPU 的运行时间划分为固定长度的时间片,每个时间片结束时,当前运行的线程会被暂停,然后调度器会选择下一个就绪的线程继续执行

基于CPU时间片方式进行线程调度,就是当只有线程得到CPU的时间片才能执行指令,当线程处于执行的状态,但是却没有分配到时间片,那么就会处于就绪状态,等待系统分配下一个时间片。

保存环境和恢复环境:当线程被切走时,系统会把当前线程的环境保存,当线程再切回来时,恢复环境。

多线程效率与cpu的内核数量和逻辑处理器数量有关 当线程数与逻辑处理器的数量相同时效率最佳 并非线程越多效率越高

img

线程的创建

线程:

UI线程(主线程):单线程的执行环境,所有的UI控件都必须在主线程上创建和更新,否则会引发跨线程访问的异常(不运行耗时长的任务)

非UI线程(工作线程):处理一些非UI相关的任务,需要自己负责管理线程的生命周期和同步机制,以避免线程间的竞争和死锁(运行耗时长的任务

创建一个计数器

img

将.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
// Counter.cpp : 定义应用程序的入口点。
//

#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;
}

img

创建线程

image-20240713155607797

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

默认栈大小:

img

线程回调

img

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使得暂停时线程的占用减小

img

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);//将剩下的切片时间让给下一个线程
//Sleep(0)几乎没什么用
}
}
}

未暂停:

img

暂停时:

img

暂停后counter.exe占用的cpu就大大减少了

线程挂起:不再分配时间切片

img

恢复

img

挂起几次对应的就得恢复几次 线程无法恢复自己(可以挂起自己)

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:
//g_bContinue = FALSE;
SuspendThread(g_hThread);
break;
case BTN_CONTINUE:
//g_bContinue = TRUE;
ResumeThread(g_hThread);
break;
case BTN_STOP:
break;
default:
break;
}
break;
case WM_CLOSE:
EndDialog(hDlg, 0);;
break;
}
return (INT_PTR)FALSE;
}

运行结果

img

线程的退出

退出线程

img

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
// Counter.cpp : 定义应用程序的入口点。
//

#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 // 0

}
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:
//g_bContinue = FALSE;
SuspendThread(g_hThread);
break;
case BTN_CONTINUE:
//g_bContinue = TRUE;
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;
}

点击开始后会出现类的构造函数

img

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

img

终止线程

img

使用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
// Counter.cpp : 定义应用程序的入口点。
//

#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:
//g_bContinue = FALSE;
SuspendThread(g_hThread);
break;
case BTN_CONTINUE:
//g_bContinue = TRUE;
ResumeThread(g_hThread);
break;
case BTN_STOP:
TerminateThread(g_hThread, 0x123456);
CloseHandle(g_hThread);
//g_bStop = TRUE;
break;
default:
break;
}
break;
case WM_CLOSE:
EndDialog(hDlg, 0);;
break;
}
return (INT_PTR)FALSE;
}

面对dll时的情况

img

img

img

点击开始时会显示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
// Counter.cpp : 定义应用程序的入口点。
//

#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 // 0

}
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:
//g_bContinue = FALSE;
SuspendThread(g_hThread);
break;
case BTN_CONTINUE:
//g_bContinue = TRUE;
ResumeThread(g_hThread);
break;
case BTN_STOP:
//TerminateThread(g_hThread, 0x123456);
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
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#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;
}