外贸网站建设 全球搜,嘉兴市做网站优化,公司的分类,泰州做企业网站的哪里好#x1f916;个人主页#xff1a;晚风相伴-CSDN博客 #x1f496;如果觉得内容对你有帮助的话#xff0c;还请给博主一键三连#xff08;点赞#x1f49c;、收藏#x1f9e1;、关注#x1f49a;#xff09;吧 #x1f64f;如果内容有误或者有写的不好的地方的话… 个人主页晚风相伴-CSDN博客 如果觉得内容对你有帮助的话还请给博主一键三连点赞、收藏、关注吧 如果内容有误或者有写的不好的地方的话还望指出谢谢 让我们共同进步 下一篇《线程的互斥和同步》敬请期待 目录
Linux线程的概念
理解Linux下的线程
线程的异常
进程VS线程
线程控制
线程相关函数
线程创建
线程等待
线程终止
获取线程ID
线程分离 Linux线程的概念 比较官方的答案线程是进程中的一条执行流它是被系统独立调度和分配的基本单位。在一个进程内的多个线程可以共享该进程所拥有的全部资源并且这些线程可以并发执行。简而言之线程是程序中一个单一的顺序执行流允许在单个程序中同时运行多个线程以完成不同的工作这种技术被称为多线程。 举个例子一个工厂里面有很多的车间每个车间里都有许多的工人每个工人各司其职完成领导交代的任务。因此这里的工人就可以想象成是线程每个车间就可以想象成是一个进程而工厂就可以想象成是一台计算机。每一台计算机内可以拥有许多的进程而在进程内部又可以有许多条执行流可以分别处理不同的任务。 理解Linux下的线程 我们知道进程具有独立性每一个进程都有自己独立的PCBtask_struct进程控制块、地址空间、页表等。 现在有这样的一个技术我们可以在进程内部创建多个task_struct并且这些task_struct共享该进程的地址空间、页表等。而这些一个个的task_struct就是线程执行流。 在Linux内核中只给进程设计了专门的数据结构而没有给线程设计专门的数据结构因为Linux的大佬认为进程和线程在很多处理上都是一样的没必要再给线程设计专门的数据结构所以Linux中的线程结构很多都是复用的进程的。因此Linux系统下没有真正意义上的线程结构而是用进程的机制模拟实现的线程。而Windows系统中有线程相关的数据结构所以Windows的结构更复杂 综上对于线程更准确的定义是线程是一个进程内部的控制序列task_struct。 线程在进程内部运行本质是在进程地址空间内运行。 线程的异常
单个线程如果出现除零、野指针问题导致崩溃进程也会随着崩溃线程是进程的执行分支线程出异常就类似进程出异常进而触发信号机制终止进程进程终止该进程内的所有线程也就随之退出。如果在线程中调用exec系列函数或者exit函数也会导致给进程退出。
可以理解为进程和线程是一体的所以一荣俱荣一损俱损
进程VS线程 进程是资源分配的基本单位而线程是调度的基本单位。 线程虽然共享进程的数据但需要有自己的一部分数据线程ID、寄存器、栈、errno、信号屏蔽字、调度优先级等。 进程的多个线程共享 同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数在各线程中都可以调用如果定义一个全局变量在各线程中都可以访问到除此之外各线程还共享以下进程资源和环境:
文件描述符表每种信号的处理方式当前的工作目录用户ID和组ID
有了线程的概念之后在看待之前的进程时就可以理解为内部只有一条执行流的进程。 在Linux系统中CPU是不区分进程和线程的因为线程也是用进程的机制来模拟实现其功能的但在CPU看来具有多条执行流的进程比单执行流的进程更加的轻量化原因如下 在CPU内部是有缓存的当执行程序时是需要先将内存中的代码和数据预读进缓存的。当一个进程的时间片到了需要被切走时要把进程对应的上下文数据全部保存起来再切走并且地址空间、页表等也都需要被切走而此时的缓存也立即失效了当下一个进程来了就需要重新将自身的代码和数据预读进缓存。而当一个线程的时间片到了需要被切走时只需要将自己的上下文数据保存起来切走就行了也不需要重新将代码和数据预读进缓存。 所以Linux下的线程也会被称为轻量级进程。 进程和线程的关系如下图 线程控制
因为Linux中是用进程机制模拟实现的线程的所以Linux并不能直接给我们提供线程相关的接口只能提供轻量级进程的接口但在用户层给我们实现了一套多线程的方案并且以库的方式提供给用户进行使用——pthread线程库原生线程库
关于线程库的一些了解
与线程有关的函数构成了一个完整的系列绝大多数函数的名字都是以“pthrad_”打头的要使用这些函数库就需要引入头文件pthread.h链接这些线程函数库是要使用编译器命令的“-lpthread”选项
线程相关函数
线程创建 参数 thread返回线程IDattr设置线程的属性attr为nullptr表示使用默认属性start_routine是一个函数地址线程启动后要执行的函数arg传给线程启动函数的参数 返回值成功返回0失败返回错误码 示例代码
#include iostream
#include pthread.h
#include string
#include cstdio
#include unistd.h
using namespace std;void *threadRun(void *args)
{const string name (char *)args;while (true){cout name , pid: getpid() endl;sleep(1);}
}int main()
{pthread_t tid[5];char name[64];for (int i 0; i 5; i){snprintf(name, sizeof name, %s-%d, thread, i 1);pthread_create(tid i, nullptr, threadRun, (void *)name);sleep(1);//缓解传参的bug问题}while (true){cout main thread, pid: getpid() endl;sleep(3);}return 0;
}
结果演示 其中ps -aL命令可以用来查看线程 LWP就是轻量级进程的意思
从结果就可以看出每次线程的顺序都是不一样的这是因为调度器调度的问题。
一旦一个线程出现了异常那么整个进程就会退出。
示例代码
void *threadRoutine(void *args)
{int i 0;while (true){cout 这是新线程: (char *)args running... endl;sleep(1);int a 10;a / 0;//除0错误}cout 新线程退出... endl;return nullptr;
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);return 0;
}
结果演示 线程等待 没错线程也是需要等待的因为如果主线程不等待即会引起类似于僵尸问题导致内存泄漏。 参数 thread线程IDretval指向一个指针该指针指向线程退出的返回值 返回值成功返回0失败返回错误码 示例代码
#include iostream
#include pthread.h
#include string
#include cstdio
#include unistd.h
using namespace std;void *threadRoutine(void *args)
{int i 0;while(true){cout 这是新线程: (char*)args running... endl;sleep(1);if(i 10) break;}cout 新线程退出... endl;return (void*)10;//线程退出的返回值
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);void* ret nullptr; pthread_join(tid, ret);cout main thread wait done ... mian quit ... new thread quit: (long long)ret endl;return 0;
}
结果演示 不只是可以返回一个数字返回一组数也是可以的。
示例代码
void *threadRoutine(void *args)
{int i 0;int *data new int[10];while (true){cout 这是新线程: (char *)args running... endl;sleep(1);data[i] i;if (i 10)break;}cout 新线程退出... endl;return (void *)data; // 线程退出的返回值
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);int *ret nullptr;pthread_join(tid, (void**)ret);cout main thread wait done ... mian quit ... new thread quit: endl;for (int i 0; i 10; i){cout ret[i] ;}cout endl;return 0;
}
结果演示 线程终止
如果需要终止某个线程而其它线程不受影响可以有三种方法
return这种方法对主线程不适用在main函数中return相当于调用exit。调用pthread_exit终止自己在一个线程中调用pthread_cancel终止同一个进程中的另一个线程。 参数 retval和线程等待那的参数一样 返回值无返回值 参数 thread线程ID 返回值成功返回0失败返回错误码 调用pthread_cancel终止的话那么pthread_join等待接收到的就是常数PTHREAD_CANCELED。 示例代码
void *threadRoutine(void *args)
{int i 0;while (true){cout 这是新线程: (char *)args running... endl;sleep(1);if (i 5)break;}cout 新线程退出... endl;pthread_exit((void*)10);
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);void *ret nullptr;pthread_join(tid, ret);cout main thread wait done ... mian quit ... new thread quit: (long long)ret endl;return 0;
}
结果演示 获取线程ID 返回值线程ID pthread_create函数会产生一个线程ID存放在第一个参数指向的地址中但是这个线程ID和ps -aL命令查看的ID是不一样的用ps -aL查看的ID是属于进程调度范畴的因为线程是轻量级进程是操作系统调度器的最小单位所以需要一个数值来唯一标识该线程。而pthread_create参数中的线程ID是指向一个虚拟内存单元这个单元是在地址空间中的共享区该内存单元的地址即为新创建线程的线程ID。 Linux线程库中提供了pthread_self函数用来获取线程自身的ID其本质是一个地址。 如图所示 示例代码
void *threadRoutine(void *args)
{cout pthread_self: pthread_self() endl;cout 这是新线程: (char *)args running... endl;pthread_exit((void*)10);
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);cout tid: tid endl;void *ret nullptr;pthread_join(tid, ret);cout main thread wait done ... mian quit ... new thread quit: (long long)ret endl;return 0;
}
结果演示 线程的局部存储 每个线程都可以拥有自己的数据使用__thread修饰全局变量就可以让每个线程中都各自拥有一个全局变量这就实现了线程的局部存储。 示例代码
__thread int g_val 10; //__thread修饰全局变量:线程的局部存储让每一个线程各自拥有一个全局变量void *threadRoutine(void *args)
{while (true){cout (char *)args : g_val 地址: g_val endl;g_val;sleep(1);break;}return nullptr;
}int main()
{pthread_t tid; // 本质上是一个地址各自线程的独立属性独立的栈、id等pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);while (true){cout main thread: g_val 地址: g_val endl;sleep(1);break;}pthread_join(tid, nullptr);return 0;
}
结果演示 线程分离
在一些场景下如果我们不关心该线程的退出结果那么我们对线程进程pthread_join就是一种负担所以就可以使用线程分离函数pthread_detach告诉操作系统让这个线程退出时自动释放资源。 参数 thread线程ID 返回值成功返回0失败返回错误码 可以是线程组内其它线程对目标线程进行分离也可以是线程自己分离。 示例代码
void *threadRoutine(void *args)
{pthread_detach(pthread_self()); // 线程分离cout 新线程退出了并且自己释放了资源 endl;return nullptr;
}int main()
{pthread_t tid; pthread_create(tid, nullptr, threadRoutine, (void *)thread 1);while (true){sleep(1);break;}int n pthread_join(tid, nullptr);cout n: n strerr: strerror(n) endl;//因为线程自己释放了资源所以pthread_join会失败return 0;
}
结果演示