C++线程
霸道小明 人气:0一、线程基本概念
线程是在进程中产生的一个执行单元,是CPU调度和分配的最小单元,其在同一个进程中与其他线程并行运行,他们可以共享进程内的资源,比如内存、地址空间、打开的文件等等。
线程是CPU调度和分派的基本单位,
进程是分配资源的基本单位
进程:正在运行的程序
是处于执行期的程序以及它所管理的资源(如打开的文件、挂起的信号、进程状态、地址空间等等)的总称,从操作系统核心角度来说,进程是操作系统调度除CPU时间片外进行的资源分配和保护的基本单位,它有一个独立的虚拟地址空间,用来容纳进程映像(如与进程关联的程序与数据),并以进程为单位对各种资源实施保护,如受保护地访问处理器、文件、外部设备及其他进程(进程间通信)
计算机有很多资源组成,比如CPU、内存、磁盘、鼠标、键盘等,就像一个工厂由电力系统、作业车间、仓库、管理办公室和工人组成
假定工厂的电力有限,一次只能供给一个或少量几个车间使用。也就是说,一部分车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务,多个CPU能够运行少量任务。
线程就好比车间里的工人。一个进程可以包括多个线程,他们协同完成某一个任务。
二、为什么使用多线程
1.避免阻塞
大家知道,单个进程只有一个主线程,当主线程阻塞的时候,整个进程也就阻塞了,无法再去做其它的一些功能了。
2.避免CPU空转
应用程序经常会涉及到RPC,数据库访问,磁盘IO等操作,这些操作的速度比CPU慢很多,而在等待这些响应时,CPU却不能去处理新的请求,导致这种单线程的应用程序性能很差。
3.提升效率
一个进程要独立拥有4GB的虚拟地址空间,而多个线程可以共享同一地址空间,线程的切换比进程的切换要快得多。
上下文切换
三、创建线程函数
1.CreateThread
CreateThread是一种微软在Windows API中提供了建立新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD SIZE_T dwStackSize,//initialstacksize LPTHREAD_START_ROUTINE lpStartAddress,//threadfunction LPVOID lpParameter,//threadargument DWORD dwCreationFlags,//creationoption LPDWORD lpThreadId//threadidentifier )
第一个参数 lpThreadAttributes 表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
第二个参数 dwStackSize 表示线程栈空间大小。传入0表示使用默认大小(1MB)。
第三个参数 lpStartAddress 表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。
第四个参数 lpParameter 是传给线程函数的参数。
第五个参数 dwCreationFlags 指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。
第六个参数 lpThreadId 将返回线程的ID号,传入NULL表示不需要返回该线程ID号
2._beginthreadex
unsigned long _beginthreadex( void *security, // 安全属性, 为NULL时表示默认安全性 unsigned stack_size, // 线程的堆栈大小, 一般默认为0 unsigned(_stdcall *start_address)(void *), // 线程函数 void *argilist, // 线程函数的参数 unsigned initflag, // 新线程的初始状态,0表示立即执行,//CREATE_SUSPENDED表示创建之后挂起 unsigned *threaddr // 用来接收线程ID );
返回值 : // 成功返回新线程句柄, 失败返回0
__stdcall表示
1.参数从右向左压入堆栈
2.函数被调用者修改堆栈
四、简单多线程示例
现在有三个任务,Tom每隔3秒捉一次Jerry,Jerry每隔2秒吃一次奶酪,Spike每隔1秒打一次Tom。分别用CreateThread和_beginthreadex实现
使用_beginthreadex
#include<stdio.h> #include<Windows.h> #include<process.h> //Tom每隔3秒捉一次老鼠 unsigned WINAPI thread_main_Tom(void* arg) { int cnt = *((int*)arg); for (int i = 0; i < cnt; i++) { Sleep(3000); puts("Tom 捉老鼠\n"); } return 0; } //Jerry每隔1秒吃一次奶酪 unsigned WINAPI thread_main_Jerry(void* arg) { int cnt = *((int*)arg); for (int i = 0; i < cnt; i++) { Sleep(1000); puts("Jerry 吃奶酪\n"); } return 0; } //Spike每隔2秒打一次猫 unsigned WINAPI thread_main_Spike(void* arg) { int cnt = *((int*)arg); for (int i = 0; i < cnt; i++) { Sleep(2000); puts("Spike 打猫\n"); } return 0; } int main() { int Tom = 20, Jerry = 50, Spike = 40; //保存线程Id unsigned int Tom_id, Jerry_id, Spike_id; //创建线程 _beginthreadex(NULL, 0, thread_main_Tom, (void*)&Tom, 0, &Tom_id); _beginthreadex(NULL, 0, thread_main_Jerry, (void*)&Jerry, 0, &Jerry_id); _beginthreadex(NULL, 0, thread_main_Spike, (void*)&Spike, 0, &Spike_id); system("pause"); return 0; }
运行结果:
使用CreateThread
#include<stdio.h> #include<Windows.h> #include<process.h> //DWORD就是unsigned long //LPVOID是void* DWORD _stdcall ThreadFun(LPVOID p) { printf("我是子线程,PID=%d", GetCurrentThreadId()); return 0; } int main() { printf("main begin\n"); HANDLE hThead; DWORD dwThreadID; hThead = CreateThread(NULL, 0, ThreadFun, 0, 0, &dwThreadID); printf("我是主线程,PID=%d\n",GetCurrentThreadId()); Sleep(2000); //关闭线程 if (hThead) { CloseHandle(hThead); } system("pause"); return 0; }
运行结果:
加载全部内容