网站建设中添加图片链接,10个免费网站,介休门户网站,vr哪家公司做得好#x1f320; 作者#xff1a;阿亮joy. #x1f386;专栏#xff1a;《学会Linux》 #x1f387; 座右铭#xff1a;每个优秀的人都有一段沉默的时光#xff0c;那段时光是付出了很多努力却得不到结果的日子#xff0c;我们把它叫做扎根 目录#x1f449;知识补充 作者阿亮joy. 专栏《学会Linux》 座右铭每个优秀的人都有一段沉默的时光那段时光是付出了很多努力却得不到结果的日子我们把它叫做扎根 目录知识补充Linux线程概念什么是线程线程 VS 进程线程的优点线程的缺点线程异常线程用途线程控制线程创建线程终止线程等待线程分离总结知识补充 Linux线程概念
什么是线程 线程是在进程内部执行的也就是说线程是在进程的地址空间内运行的其是操作系统调度的基本单位。进程等于内核数据结构加上该进程对应的代码和数据内核数据结构可能不止一个 PCB进程是承担分配系统资源的基本实体将资源分配给线程那如何理解我们之前写的代码呢其实我们之前学习的是只有一个执行流的进程而今天学习的是具有多个执行流的进程task_struct 是进程内部的一个执行流所以这两者是不冲突的。在运行队列中排队的都是 task_structCPU 只能看到 task_structCPU 根本不关系当前调度的是进程还是线程只关心 task_struct。所以CPU 调度的基本单位是”线程”。Linux 下的线程是轻量级进程没有真正意义上的线程结构没有为线程专门设计内核数据结构而是通过 PCB 来模拟实现出线程的。Linux 并不能直接给我们提供线程相关的接口只能提供轻量级进程的接口在用户层实现了一套多进程方案以库的方式提供给用户进行使用这个库就是 pthread 线程库原生线程库。 知道了什么是线程我们来学习创建线程的接口来验证一下上面的结论 pthread_create 函数的功能是创建一个新的进程。thread 是输出型参数返回进程的 IDattr 设置线程的属性attr 为 nullptr 表示使用默认属性start_routine 是一个函数地址即线程启动后要执行的函数arg 是传给线程启动函数的参数。调用成功是返回 0错误是返回错误码。 Makefile
mythread:mythread.ccg $^ -o $ -stdc11 -lpthread
.PHONY:clean
clean:rm -f mythread注使用原生线程库时必须带上 -lpthread告诉编译器你要链接原生线程库否则就会产生链接错误。 // 注一下代码是示例代码,有些许问题
#include iostream
#include pthread.h
#include unistd.h
#include cstdio
#include stringusing namespace std;void* threadRun(void* args)
{string name (char*)args;while(1){cout name id: getpid() \n endl;sleep(1);}return nullptr;
}int main()
{pthread_t tid[5];char name[64];for (int i 0; i 5; i){snprintf(name, sizeof name, %s-%d, thread, i);pthread_create(tid i, nullptr, threadRun, (void*)name);sleep(3); // 缓解传参的bug}while (true){cout main thread, pid: getpid() endl;sleep(3);}return 0;
}ps -aL | head -1 ps -aL | grep mythread | grep -v grep #查找线程将进程 16889 杀掉时全部执行流都会终止。因为线程用的资源都是进程给的而杀掉进程就要回收进程的资源那么线程终止了是理所当然的。
线程是如何看到进程内部的资源的呢
我们知道线程的运行依赖于进程的资源一旦进程退出线程也会退出。那进程的哪些资源是线程之间共享的哪些资源又是线程独自占用的呢
进程的大多数资源都被线程所共享
文件描述符表如果一个线程打开了一个文件那么其他的线程也能够看到。每种信号的处理方式SIG_IGN、SIG_DFL 或者自定义的信号处理函数当前工作目录用户 ID 和组 ID进程地址空间的代码区、共享区已初始化、未初始化数据区也就是全局变量堆区一般也是被所有线程共享的但在使用时认为线程申请的堆空间是线程私有的因为只有这个线程拿到这段空间的其实地址
线程独自占用的资源
线程 ID一组寄存器。线程是 CPU 调度的基本单位一个线程被调度一定会形成自己的上下文那么这组寄存器必须是私有的才能保证正常的调度。栈。每个线程都是要通过函数来完成某种任务的函数中会定义各种临时变量那么线程就需要有自己私有的栈来保存这些局部变量。错误码 errno、信号屏蔽字、调度优先级
线程 VS 进程
为什么线程的调度切换的成本更低呢
线程进行切换时进程地址空间和页表是不用换的。而进程进行切换时需要将进程的上下文进程地址空间、页表、PCB 等都要切换。CPU 内部是有 L1 ~ L3 的 CacheCPU 执行指令时会更具局部性原理将内存中的代码和数据预读到 CPU 的缓存中。如果是多线程CPU 预读的代码和数据很大可能就会被所有的线程共享那么进行线程切换时下一个线程所需要的代码和数据很有可能已经被预读了这样线程切换的成本就会更低而进程具有独立性进行进程切换时CPU 的 Cache 缓存的代码和数据就会立即失效需要将新进程的代码和数据重新加载到 Cache 中所以进程切换的成本是更高的。
进程和线程的关系如下图 线程的优点 创建一个新线程的代价要比创建一个新进程小得多与进程之间的切换相比线程之间的切换需要操作系统做的工作要少很多线程占用的资源要比进程少很多能充分利用多处理器的可并行数量在等待慢速 I / O 操作结束的同时程序可执行其他的计算任务计算密集型应用为了能在多处理器系统上运行将计算分解到多个线程中实现I / O 密集型应用为了提高性能将 I / O 操作重叠。线程可以同时等待不同的 I / O 操作 注线程不是创建越多越好因为线程切换也是有成本的并不是不需要成本。创建线程太多了线程切换的成本有可能就是最大的成本了。
线程的缺点
性能损失一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多那么可能会有较大的性能损失这里的性能损失指的是增加了额外的同步和调度开销而可用的资源不变。健壮性降低编写多线程需要更全面更深入的考虑在一个多线程程序里因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的换句话说线程之间是缺乏保护的。如一个线程对全局变量修改了另外的线程的全局变量也会跟着修改还有就是如果主线程挂掉了其他线程也会跟着挂掉。缺乏访问控制进程是访问控制的基本粒度在一个线程中调用某些操作系统函数会对整个进程造成影响。编程难度提高编写与调试一个多线程程序比单线程程序困难得多。
线程异常 单个线程如果出现除零野指针问题导致线程崩溃进程也会随着崩溃。线程是进程的执行分支线程出异常就类似进程出异常进而触发信号机制终止进程。进程终止该进程内的所有线程也就随即退出。 线程用途 合理的使用多线程能提高 CPU 密集型程序的执行效率。合理的使用多线程能提高 I / O 密集型程序的用户体验如生活中我们一边写代码一边下载开发工具就是多线程运行的一种表现。 线程控制 clone 函数可以创建线程或者子进程可以设置回调函数子进程的栈区还有各种属性等等。除了 clone 函数还有一个 vfork 函数。vfork 函数创建出来的子进程是和父进程共享进程地址空间的。
#include iostream
#include string
#include unistd.h
#include cassert
#include sys/types.h
#include sys/wait.husing namespace std;int globalVal 100;int main()
{int id vfork();// int id fork();assert(id ! -1);if(id 0){// child processint count 0;while(1){cout child process - globalVal: globalVal endl;sleep(1);count;if(count 5){globalVal 200;cout child process change globalVal! endl;exit(1);}}}//waitpid(id, nullptr, 0); // 为了演示现象就不等待子进程了// parent processwhile(1){cout parent process - globalVal: globalVal endl;sleep(1);}return 0;
}线程创建
线程创建的函数在上面已经提过了就不在赘述了。我们也已经知道通过 kill 命令来杀掉进程其余线程也会跟着终止。那么现在我们就来验证一下线程出现异常导致进程终止。
#include iostream
#include pthread.h
#include unistd.husing namespace std;void* threadRoutine(void* args)
{while(1){cout 新线程: (char*)args running ... endl;sleep(1);int a 100;a / 0;}
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void*)thread one);while(1){cout 主线程: running ... endl;sleep(1); }return 0;
}结论线程谁先运行与调度器相关。线程一旦异常都有可能导致整个进程整体退出 线程终止 如果需要只终止某个线程而不终止整个进程可以有三种方法 从线程函数 return。这种方法对主线程不适用从main 函数 return 相当于调用 exit。线程可以调用 pthread_ exit 终止自己。一个线程可以调用 pthread_ cancel 终止同一进程中的另一个线程。 注在多线程场景下不要使用 exit 函数exit 函数是终止整个进程的 pthread_exit 函数 pthread_exit 函数的功能是终止线程。retvalretval 不要指向一个局部变量。无返回值跟进程一样线程结束的时候无法返回到它的调用者自身。 #include iostream
#include pthread.h
#include unistd.husing namespace std;void* threadRoutine(void* args)
{int i 0;while(1){cout 新线程: (char*)args running ... endl;sleep(1);if(i 3) break;}cout (char*)args quit endl;pthread_exit((void*)10);
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void*)thread one);void* ret nullptr;pthread_join(tid, ret);cout ret: (long long)ret main thread wait done... main quit too endl;return 0;
}pthread_cancel 函数 pthread_cancel 函数的功能是取消一个执行中的线程。thread 是线程的 ID调用成功是返回 0失败是返回错误码。 #include iostream
#include pthread.h
#include unistd.husing namespace std;void* threadRoutine(void* args)
{int i 0;while(1){cout 新线程: (char*)args running ... endl;sleep(1);}cout (char*)args quit endl;pthread_exit((void*)13);
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void*)thread one);// pthread_cancel(tid); // 不要一创建线程就取消它int count 0;while(1){cout main线程 running ... endl;sleep(2);count;if(count 5) break;}pthread_cancel(tid);cout pthread cancel tid: tid endl;void* ret nullptr;pthread_join(tid, ret);cout ret: (long long)ret main thread wait done... main quit too endl;return 0;
}当一个线程被取消时线程的退出结果是 -1PTHREAD_CANCELED。使用 pthread_cancel 函数的前提是线程已经跑起来了才能够取消所以不要穿甲一个线程后就立马取消可能刚创建的线程还没有跑起来。一般情况下都是用主线程来取消新线程的。如果使用新线程来取消主线程的话这样会影响整个进程。
线程 ID 的深入理解
线程 ID 本质是一个地址因为我们目前用的不是 Linux 自带的创建线程的接口用的是 pthread 库中的接口用户需要的是线程而 Linux 系统只提供轻量级进程无法完全表示线程所以在用户和操作系统之间加了个软件层 pthread 库。操作系统承担轻量级进程的调度和内核数据结构的管理而线程库要给用户提供线程相关的属性字段包括线程 ID、栈的大小等等。 pthread_self 函数可以获取当前线程的 ID既然能获得当前线程的 ID那么线程就可以自己取消自己但是这种方式不推荐 线程局部存储用 __thread 修饰全局变量带来的结果就是让每一个线程各自拥有一个全局变量这就是线程的局部存储。
#include iostream
#include pthread.h
#include unistd.husing namespace std;__thread int g_val 0;void* threadRoutine(void* args)
{while(1){cout (char*)args g_val: g_val g_val: g_val endl;g_val;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void*)thread 1);while(1){cout main thread g_val: g_val g_val: g_val endl;sleep(2);}pthread_join(tid, nullptr);return 0;
} 去掉 __thread 修饰后所有线程看到的全局变量都是同一个__thread 所有 pthread 库给 g 编译器的一个编译选项
在多线程的场景下进行进程替换
#include iostream
#include pthread.h
#include unistd.husing namespace std;__thread int g_val 0;void* threadRoutine(void* args)
{sleep(5);execl(/bin/ls, ls, -l, nullptr);while(1){cout (char*)args g_val: g_val g_val: g_val endl;g_val;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void*)thread 1);while(1){cout main thread g_val: g_val g_val: g_val endl;sleep(1);}pthread_join(tid, nullptr);return 0;
} 在多线程的场景下执行进程替换那么先会将除主线程外的其它线程都终止掉然后再进行进程替换。 线程等待
线程在创建并执行的时候线程也是需要被等待的。如果不等待线程的话会引起类似于进程的僵尸问题进而导致内存泄漏。已经退出的线程其空间没有被释放仍然在进程的地址空间内。创建新的线程不会复用刚才退出线程的地址空间。 pthread_join 函数的功能是等待线程结束。thread 是要线程的 IDretval 指向线程所执行的函数的返回值。调用该函数的线程将阻塞等待直到 ID为 thread 的线程终止。thread 线程以不同的方法终止通过 pthread_join 得到的终止状态是不同的总结如下 如果 thread 线程通过 return 返回retval 所指向的单元里存放的是 thread 线程函数的返回值。如果 thread 线程被别的线程调用 pthread_ cancel 异常终掉retval 所指向的单元里存放的是常数 PTHREAD_ CANCELED。如果 thread 线程是自己调用 pthread_exit 终止的retval 所指向的单元存放的是传给 pthread_exit 的参数。如果对 thread 线程的终止状态不感兴趣可以传 nullptr 给 retval 参数。thread 线程函数的返回值不会考虑异常的情况如果线程出现了异常那么整个进程都会崩掉。注状态寄存器是所有线程共享的。 #include iostream
#include pthread.h
#include unistd.husing namespace std;void* threadRoutine(void* args)
{int i 0;while(1){cout 新线程: (char*)args running ... endl;sleep(1);if(i 6) break;}cout (char*)args quit endl;return nullptr;
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void*)thread one);pthread_join(tid, nullptr); // 默认会阻塞等待cout main thread wait done... main quit too endl;return 0;
}线程执行的函数的返回值是返回给主线程的主线程通过该返回值来获取线程退出的状态。
#include iostream
#include pthread.h
#include unistd.husing namespace std;void* threadRoutine(void* args)
{int i 0;while(1){cout 新线程: (char*)args running ... endl;sleep(1);if(i 6) break;}cout (char*)args quit endl;return (void*)10;
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void*)thread one);void* ret nullptr;pthread_join(tid, ret);cout ret: (long long)ret main thread wait done... main quit too endl;return 0;
}线程执行的函数的返回值可以多种多样比如返回一段堆空间的起始地址。
#include iostream
#include pthread.h
#include unistd.husing namespace std;void* threadRoutine(void* args)
{int i 0;int* ret new int[7];while(1){cout 新线程: (char*)args running ... endl;sleep(1);ret[i] i;if(i 6) break;}cout (char*)args quit endl;return (void*)ret;
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void*)thread one);int* ret nullptr;pthread_join(tid, (void**)ret);for(int i 0; i 7; i)cout ret[i] ;cout endl;cout ret: (long long)ret main thread wait done... main quit too endl;return 0;
}线程分离 默认情况下新创建的线程是 joinable 的线程退出后需要对其进行 pthread_join 操作否则无法释放资源从而造成系统泄漏。如果不关心线程的返回值join 是一种负担。这个时候我们可以告诉系统当线程退出时自动释放线程资源这就是线程分离。一般主线程时不退出的当用户有个任务要处理主线程就可以创建新线程来执行用户的任务但主线程不关心任务处理的结果那么就可以将该线程分离出去。 #include iostream
#include pthread.h
#include unistd.h
#include cerrno
#include cstringusing namespace std;__thread int g_val 0;void* threadRoutine(void* args)
{pthread_detach(pthread_self());while(1){cout (char*)args g_val: g_val g_val: g_val endl;g_val;break;}pthread_exit((void*)11);
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void*)thread 1);while(1){cout main thread g_val: g_val g_val: g_val endl;sleep(1);break;}int n pthread_join(tid, nullptr);cout n: n error string: strerror(n) endl;return 0;
} 注joinable 和分离是冲突的一个线程不能既是joinable 又是分离的。
如果线程被分离但是该线程出现了异常这样也会影响到整个进程。线程执行的是进程派发的任务尽管线程被分离了线程也离不开进程的资源所以线程出现了异常也会导致进程终止。 注C 11 的线程库也是调用了原生线程库的所以在使用 C 的线程库时也要指定链接原生线程库。
总结 本篇博客主要讲解了什么是线程、线程和进程的区别、线程的优缺点、线程异常、线程用途以及线程控制等。那么以上就是本篇博客的全部内容了如果大家觉得有收获的话可以点个三连支持一下谢谢大家❣️
文章转载自: http://www.morning.zfcfx.cn.gov.cn.zfcfx.cn http://www.morning.mhrzd.cn.gov.cn.mhrzd.cn http://www.morning.pqcsx.cn.gov.cn.pqcsx.cn http://www.morning.zcfmb.cn.gov.cn.zcfmb.cn http://www.morning.ysllp.cn.gov.cn.ysllp.cn http://www.morning.rqqct.cn.gov.cn.rqqct.cn http://www.morning.mhsmj.cn.gov.cn.mhsmj.cn http://www.morning.mqwnp.cn.gov.cn.mqwnp.cn http://www.morning.bnjnp.cn.gov.cn.bnjnp.cn http://www.morning.mnnxt.cn.gov.cn.mnnxt.cn http://www.morning.5-73.com.gov.cn.5-73.com http://www.morning.krswn.cn.gov.cn.krswn.cn http://www.morning.twwzk.cn.gov.cn.twwzk.cn http://www.morning.smdnl.cn.gov.cn.smdnl.cn http://www.morning.nfyc.cn.gov.cn.nfyc.cn http://www.morning.vibwp.cn.gov.cn.vibwp.cn http://www.morning.pudejun.com.gov.cn.pudejun.com http://www.morning.jgmlb.cn.gov.cn.jgmlb.cn http://www.morning.brkrt.cn.gov.cn.brkrt.cn http://www.morning.rbylq.cn.gov.cn.rbylq.cn http://www.morning.jkfyt.cn.gov.cn.jkfyt.cn http://www.morning.lrnfn.cn.gov.cn.lrnfn.cn http://www.morning.qjfkz.cn.gov.cn.qjfkz.cn http://www.morning.dwzwm.cn.gov.cn.dwzwm.cn http://www.morning.rrxmm.cn.gov.cn.rrxmm.cn http://www.morning.ldwxj.cn.gov.cn.ldwxj.cn http://www.morning.bkjhx.cn.gov.cn.bkjhx.cn http://www.morning.wqcz.cn.gov.cn.wqcz.cn http://www.morning.qnksk.cn.gov.cn.qnksk.cn http://www.morning.ckbmz.cn.gov.cn.ckbmz.cn http://www.morning.ctfh.cn.gov.cn.ctfh.cn http://www.morning.ykrss.cn.gov.cn.ykrss.cn http://www.morning.qzqjz.cn.gov.cn.qzqjz.cn http://www.morning.hdqqr.cn.gov.cn.hdqqr.cn http://www.morning.bhmnp.cn.gov.cn.bhmnp.cn http://www.morning.tnqk.cn.gov.cn.tnqk.cn http://www.morning.qbkw.cn.gov.cn.qbkw.cn http://www.morning.hqnsf.cn.gov.cn.hqnsf.cn http://www.morning.pcgrq.cn.gov.cn.pcgrq.cn http://www.morning.xrrjb.cn.gov.cn.xrrjb.cn http://www.morning.mhnb.cn.gov.cn.mhnb.cn http://www.morning.feites.com.gov.cn.feites.com http://www.morning.lxthr.cn.gov.cn.lxthr.cn http://www.morning.rnngz.cn.gov.cn.rnngz.cn http://www.morning.zcckq.cn.gov.cn.zcckq.cn http://www.morning.nfgbf.cn.gov.cn.nfgbf.cn http://www.morning.tkrpt.cn.gov.cn.tkrpt.cn http://www.morning.llqky.cn.gov.cn.llqky.cn http://www.morning.lkrmp.cn.gov.cn.lkrmp.cn http://www.morning.tzzfy.cn.gov.cn.tzzfy.cn http://www.morning.lwdzt.cn.gov.cn.lwdzt.cn http://www.morning.wzknt.cn.gov.cn.wzknt.cn http://www.morning.wrfk.cn.gov.cn.wrfk.cn http://www.morning.junmap.com.gov.cn.junmap.com http://www.morning.zpstm.cn.gov.cn.zpstm.cn http://www.morning.dytqf.cn.gov.cn.dytqf.cn http://www.morning.drcnn.cn.gov.cn.drcnn.cn http://www.morning.rklgm.cn.gov.cn.rklgm.cn http://www.morning.gkmwx.cn.gov.cn.gkmwx.cn http://www.morning.xqbbc.cn.gov.cn.xqbbc.cn http://www.morning.zztkt.cn.gov.cn.zztkt.cn http://www.morning.ydnxm.cn.gov.cn.ydnxm.cn http://www.morning.wjpsn.cn.gov.cn.wjpsn.cn http://www.morning.pkfpl.cn.gov.cn.pkfpl.cn http://www.morning.qsmmq.cn.gov.cn.qsmmq.cn http://www.morning.qsmdd.cn.gov.cn.qsmdd.cn http://www.morning.rbrd.cn.gov.cn.rbrd.cn http://www.morning.kzrg.cn.gov.cn.kzrg.cn http://www.morning.npcxk.cn.gov.cn.npcxk.cn http://www.morning.zrkws.cn.gov.cn.zrkws.cn http://www.morning.fthcn.cn.gov.cn.fthcn.cn http://www.morning.lffgs.cn.gov.cn.lffgs.cn http://www.morning.lmmh.cn.gov.cn.lmmh.cn http://www.morning.lskyz.cn.gov.cn.lskyz.cn http://www.morning.xtdms.com.gov.cn.xtdms.com http://www.morning.nhzzn.cn.gov.cn.nhzzn.cn http://www.morning.qlkzl.cn.gov.cn.qlkzl.cn http://www.morning.srgyj.cn.gov.cn.srgyj.cn http://www.morning.bxyzr.cn.gov.cn.bxyzr.cn http://www.morning.sjpbh.cn.gov.cn.sjpbh.cn