当前位置:   article > 正文

C++基础16:C++多线程处理_c++多线程处理class

c++多线程处理class

此专栏为移动机器人知识体系下的编程语言中的 C {\rm C} C++从入门到深入的专栏,参考书籍:《深入浅出 C {\rm C} C++》(马晓锐)和《从 C {\rm C} C C {\rm C} C++精通面向对象编程》(曾凡锋等)。



15.C++多线程处理
15.1 进程和线程基础
  • 进程是操作系统资源分配和调用的基本单位,其独立享有系统分配的资源;线程是从属于进程的一个独立执行单位,可以与其从属进程的其他线程共享资源,线程是 C P U {\rm CPU} CPU调度的基本单位;

  • 当一个进程被建立后,其包含进程控制块、程序块、数据段和其他资源,进程占用系统一定的资源,这些资源在进程启动时创建,在进程终止时被系统回收;

  • 线程是进程中的一个相对独立的执行单位,它能独立地处理某个任务,线程由进程创建,并受到进程的管制;

  • 一个进程可以创建多个线程,但至少有一个线程,当一个进程启动后,首先生成一个默认的线程,这个线程称为主线程;

  • 多线程可以让一个应用程序同时处理几个任务,如:一个通信程序,可以建立两个线程,一个负责接收并处理数据,一个负责发送数据,相互独立,互不影响;

  • 一个进程可以包含多个线程,这些线程享有进程的系统资源,包括 C P U {\rm CPU} CPU资源;当一个进程中包含多个线程时,由操作系统为每个线程分配 C P U {\rm CPU} CPU执行时间片,在某一个时刻只能执行一个线程的代码,多个线程在 C P U {\rm CPU} CPU中轮流执行,这些执行线程的 C P U {\rm CPU} CPU时间片和线程切换时间非常短,宏观上几乎可以忽略不计;

  • 线程的优先级是操作系统为线程分配 C P U {\rm CPU} CPU时间片的依据,优先级高的线程会优先得到 C P U {\rm CPU} CPU的执行时间片,优先级越高,在同一个时间片竞争中获得 C P U {\rm CPU} CPU资源的概率越大;

  • 线程从创建到消亡,存在于不同的状态,如下图所示:

    21

    • 线程被创建:此时线程被进程创建,分配了相关的资源,但未开始运行;
    • 线程运行:线程被启动开始工作;
    • 线程挂起(亦称睡眠):线程在运行期间收到挂起命令,则立即停止运行并等待新的命令,当挂起的线程收到恢复命令时,则重新转入运行;
    • 线程结束:当处于运行或挂起线程收到结束命令时,则终止线程;
15.2 线程的操作
15.2.1 线程的建立
  • 用一个初始函数创建线程,用初始函数创建线程时,直接用函数名进行类对象的实例化,语法格式如下:

    return_type function_name(parameter list)
    {
        函数体;
    }
    
    • 1
    • 2
    • 3
    • 4
    • r e t u r n _ t y p e {\rm return\_type} return_type:返回值类型,一个函数可以返回一个值, r e t u r n _ t y p e {\rm return\_type} return_type是函数返回值的数据类型;
    • f u n c t i o n _ n a m e {\rm function\_name} function_name:函数名称,函数名和参数列表一起构成函数;
    • p a r a m e t e r   l i s t {\rm parameter\ list} parameter list:参数列表,参数列表包括函数参数类型、顺序和数量;
    • 函数体:函数体包含一组定义函数执行任务的语句;
  • 初始函数创建线程实例,使用 t h r e a d {\rm thread} thread库创建一个线程 ( e x a m p l e 15 _ 1. c p p ) ({\rm example15\_1.cpp}) (example15_1.cpp)

    /**
     * 作者:罗思维
     * 时间:2024/04/05
     * 描述:使用thread库建立一个线程。 
     */
    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    // 线程函数; 
    void print_thread() {
    	cout << "线程1执行." << endl;
    	cout << "线程2执行." << endl;
    	cout << "线程3执行." << endl;
    }
    
    int main() {
    	// 创建线程对象,用线程函数实例化; 
    	thread my_thread(print_thread);
    	
    	// 阻塞主线程; 
    	my_thread.join();
    
    	cout << "主线程执行." << endl;
    
    	return 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
    • t h r e a d   m y _ t h r e a d ( p r i n t _ t h r e a d ) {\rm thread\ my\_thread(print\_thread)} thread my_thread(print_thread):创建一个线程 m y _ t h r e a d {\rm my\_thread} my_thread ( p r i n t _ t h r e a d ) ({\rm print\_thread}) (print_thread)是该线程的初始函数;
    • m y _ t h r e a d . j o i n ( ) {\rm my\_thread.join()} my_thread.join():阻塞主线程,等待线程 m y _ t h r e a d ( ) {\rm my\_thread()} my_thread()执行完毕,主线程再继续执行;
    • 此程序的主线程指 m a i n {\rm main} main函数,子线程指 m y _ t h r e a d {\rm my\_thread} my_thread对象;
  • 用类对象创建一个线程,用类对象创建线程时,用对象名对线程对象实例化,实例如下 ( e x a m p l e 15 _ 2. c p p ) ({\rm example15\_2.cpp}) (example15_2.cpp)

    /**
     * 作者:罗思维
     * 时间:2024/04/05
     * 描述:用类对象创建一个线程。 
     */
    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    class TT {
    	public:
    		int it;
    		// 构造函数; 
    		TT(int m_it): it(m_it) {
    			cout << "构造函数被执行." << endl;
    		}
    		
    		// 复制构造函数; 
    		TT(const TT& t): it(t.it) {
    			cout << "复制构造函数被执行." << endl;
    		}
    
    		// 析构函数; 
    		~TT() {
    			cout << "析构函数被执行." << endl;
    		}
    
    		// 重载运算符(); 
    		void operator()() {
    			cout << "it的值:" << it << endl;
    		}
    };
    int main() {
    	int it = 6;
    	TT tt(it);					// 调用构造函数; 
    
    	thread my_thread(tt);		// 创建线程对象并用类对象实例; 
    	my_thread.join();			// 阻塞主线程main函数; 
    
    	cout << "主线程执行." << endl;
    
    	return 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
  • l a m b d a {\rm lambda} lambda表达式创建一个线程,实例如下 ( e x a m p l e 15 _ 3. c p p ) ({\rm example15\_3.cpp}) (example15_3.cpp)

    /**
     * 作者:罗思维
     * 时间:2024/04/05
     * 描述:用lambda表达式创建一个线程。 
     */
    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    int main() {
    	// 用lambda表达式创建一个线程; 
    	auto my_lam_thread = [] {
    		cout << "线程开始执行." << endl;
    		cout << "线程执行结束." << endl;
    	};
    	
    	// 创建线程对象并用lambda表达式实例化; 
    	thread my_thread(my_lam_thread);
    	my_thread.join();
    
    	cout << "主线程执行结束." << endl;
    
    	return 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
  • C r e a t e T h r e a d ( ) {\rm CreateThread()} CreateThread()函数创建线程,实例如下 ( e x a m p l e 15 _ 4. c p p ) ({\rm example15\_4.cpp}) (example15_4.cpp)

    // CreateThread()函数原型:
    HANDLE CreateThread(
      LPSECURITY_ATTRIBUTES   lpThreadAttributes,      	// 指向安全属性的指针
      SIZE_T                  dwStackSize,              // 初始化栈的大小
      LPTHREAD_START_ROUTINE  lpStartAddress,           // 线程函数指针
      LPVOID                  lpParameter,              // 传递给线程函数的参数
      DWORD                   dwCreationFlags,          // 创建标志
      LPDWORD                 lpThreadId               	// 用于存储新线程ID的变量
    );
    
    // 参数说明:
    // lpThreadAttributes:指向_SECURITY_ATTRIBUTES结构体的指针,一般设置为NULL;
    // dwStackSize:设定线程堆栈的大小,一般设置为0,系统自动进行调整其大小;
    // lpStartAddress:指向线程函数的指针,调用时将线程函数的地址传入,传入函数名即可;
    // lpParameter:指向线程函数的结构体指针,如果没有参数,设置为NULL,若有,则将参数列表定义成一个结构体传入;
    // dwCreationFlags:线程建立时的状态标志,可设为0、CREATE_SUSPENDED、THREAD_TERMINATE等,
    // 当设置为0时,创建线程后立即被激活;当设置为CREATE_SUSPENDED时,线程创建后立即被挂起;
    // lpThreadId:返回创建的这个线程的句柄,创建失败返回false;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    /**
     * 作者:罗思维
     * 时间:2024/04/05
     * 描述:使用CreateThread创建两个线程。 
     */
    #include <iostream>
    #include <Windows.h>
    #include <thread>
    
    using namespace std;
    
    // 定义结构体存储传入线程函数的参数; 
    typedef struct _param {
    	long lVal1;
    	char cVal2;
    } PARAM, * PPARAM;
    
    // 定义线程函数; 
    DWORD WINAPI ThreadProc(LPVOID lpParam) {
    	PPARAM pParam;
    	pParam = (PPARAM)lpParam;
    
    	return 0;
    }
    
    int main() {
    	HANDLE hThread[2];
    	DWORD dwThreadId[2];
    
    	PPARAM pParam = new PARAM;
    	pParam->lVal1 = 1000;
    	pParam->cVal2 = 'W';
    
    	hThread[0] = CreateThread(
    	                 NULL,				// 安全属性; 
    	                 0,					// 线程栈大小; 
    	                 ThreadProc,		// 线程函数地址; 
    	                 pParam,			// 传递参数; 
    	                 0,					// 状态标志; 
    	                 &dwThreadId[0]		// 线程标识ID; 
    	             );
    
    	pParam->lVal1 = 2000;
    	pParam->cVal2 = 'E';
    
    	hThread[1] = CreateThread(
    	                 NULL,
    	                 0,
    	                 ThreadProc,
    	                 pParam,
    	                 0,
    	                 &dwThreadId[1]
    	             );
    
    	// 等待线程完成返回; 
    	WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
    
    	// 关闭线程; 
    	CloseHandle(hThread[0]);
    	CloseHandle(hThread[1]);
    
    	return 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
    • 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
15.2.2 线程优先级设定
// 1.设定线程优先级的API函数:
BOOL WINAPI SetThreadPriority(
    HANDLE hThread,
    int nPriority
);

// 2.获取线程优先级的API函数:
Int WINAPI GetThreadPriority(
    HANDLE hThread
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
15.2.3 线程的挂起和恢复
// 1.线程挂起函数SuspendThread(),函数原型如下:
// SuspendThread()函数用来挂起线程,即暂停线程的执行;
DWORD WINAPI
SuspendThread(
    HANDLE hThread
);

// 2.线程恢复函数ResumeThread(),函数原型如下:
// ResumeThread()函数用来恢复已经挂起的线程使其重新开始运行;
DWORD WINAPI
ResumeThread(
    HANDLE hThread
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

线程挂起和恢复实例 ( e x a m p l e 15 _ 5. c p p ) ({\rm example15\_5.cpp}) (example15_5.cpp)

/**
 * 作者:罗思维
 * 时间:2024/04/05
 * 描述:线程的挂起和恢复。 
 */
#include <iostream>
#include <thread>
#include <Windows.h>

using namespace std;

DWORD WINAPI ThreadProc(LPVOID lpParam) {
	int pParam;
	pParam = *(int*)(lpParam);
	while (true) {
		printf("Thread %d \n", pParam);
		Sleep(1000);
	}

	return 0;
}
int main() {
	HANDLE hThread[2];
	DWORD dwThreadId[2];
	int nParam[2] = {1, 2};
	hThread[0] = CreateThread(
	                 NULL,
	                 0,
	                 ThreadProc,
	                 &nParam[0],
	                 0,
	                 &dwThreadId[0]
	             );
	Sleep(100);

	hThread[1] = CreateThread(
	                 NULL,
	                 0,
	                 ThreadProc,
	                 &nParam[1],
	                 0,
	                 &dwThreadId[1]
	             );
	Sleep(2000);

	SuspendThread(hThread[0]);				// 挂起;
	cout << "Thread 1 Suspend." << endl;

	Sleep(2000);

	ResumeThread(hThread[0]);				// 恢复;
	cout << "Thread 1 Resume." << endl;

	Sleep(2000);

	TerminateThread(hThread[0], 0); 		// 强制终止线程;
	TerminateThread(hThread[1], 0);

	WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
	CloseHandle(hThread[0]);
	CloseHandle(hThread[1]);

	return 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
  • 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
// 运行结果:
Thread 1
Thread 2
Thread 1
Thread 2
Thread 1
Thread 1 Suspend.
Thread 2
Thread 2
Thread 1 Resume.
Thread 2
Thread 1
Thread 2
Thread 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
15.2.4 线程的结束
  • 调用 T e r m i n a t e T h r e a d ( ) {\rm TerminateThread()} TerminateThread()函数结束线程,其原型为:

    BOOL WINAPI
    TerminateThread(
        HANDLE hThread,
        DWORD dwExitCode
    );
    
    // 参数说明:
    // dwExitCode:设定线程的退出码;
    // TerminateThread()函数在终止线程时,不释放线程占用的资源,大部分情况下它是不安全的;
    // 如果使用此函数,需要再调用CloseHandle()函数释放线程的堆栈资源;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 使用 E x i t T h r e a d ( ) {\rm ExitThread()} ExitThread()函数结束线程,函数原型为:

    VOID WINAPI
    ExitThread(
        DWORD dwExitCode
    );
    
    // ExitThread()函数用于线程自我终止的情况,主要在线程内被调用;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 使用全局变量结束线程,改变全局变量使线程的执行函数返回,则该线程终止;

15.3 实战

项目需求:在一个程序中开辟两个线程:一个线程用于计算并显示奇数,另一个线程用于计算并显示偶数,当计算数字超过 10 10 10时,各自退出线程。

代码实现 ( e x a m p l e 15 _ 6. c p p ) ({\rm example15\_6.cpp}) (example15_6.cpp)

/**
 * 作者:罗思维
 * 时间:2024/04/05
 * 描述:一个线程用于计算并显示奇数,一个线程用于计算并显示偶数。
 */
#include <iostream>
#include <string>
#include <process.h>
#include <windows.h>
#include <thread>

#define THREADS_NUM 2

using namespace std;

void __cdecl thread1(void *params);
void __cdecl thread2(void *params);

int main() {
	HANDLE hThreads[THREADS_NUM];

	hThreads[0] = (HANDLE)_beginthread(thread1, 0, NULL);
	hThreads[1] = (HANDLE)_beginthread(thread2, 0, NULL);

	Sleep(1000);

	CloseHandle(hThreads[0]);
	CloseHandle(hThreads[1]);

	return 0;
}

void __cdecl thread1(void *params) {
	int n = 0;
	while (n <= 10) {
		if (n / 2 == 0) {
			printf("Thread1-%d\n", n);
			//cout << "Thread1-" << n << endl;
		}
		n++;
		Sleep(2);
	}
	printf("Thread1 exit.\n");
	return;
}

void __cdecl thread2(void *params) {
	int n = 0;
	while (n <= 10) {
		if (n / 2 != 0) {
			printf("Thread2-%d\n", n);
			//cout << "Thread1-" << n << endl;
		}
		n++;
		Sleep(2);
	}
	printf("Thread2 exit.\n");

	return;
}
  • 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
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/1014162
推荐阅读
相关标签
  

闽ICP备14008679号