建设网站要服务器,html商品页面代码,网站建设公司市场策划方案,服务公司理念多线程控制
回顾一下线程的概念
线程是CPU调度的基本单位#xff0c;进程是承担分配系统资源的基本单位。linux在设计上并没有给线程专门设计数据结构#xff0c;而是直接复用PCB的数据结构。每个新线程#xff08;task_struct{}中有个指针都指向虚拟内存mm_struct结构进程是承担分配系统资源的基本单位。linux在设计上并没有给线程专门设计数据结构而是直接复用PCB的数据结构。每个新线程task_struct{}中有个指针都指向虚拟内存mm_struct结构实现了共享同一份代码拥有该进程的一部分资源
在linux中把所有执行流都看作是轻量级进程故有一个用户级的原生线程库为用户提供“线程”接口对OS是轻量级线程。
从信号、异常和资源看线程的健壮性问题
一个线程出现异常会影响其他线程
#include iostream
#include string
#include pthread.h
#include unistd.husing namespace std;void* start_routime(void* args)
{string name static_castconst char*(args);//安全地进行强制类型转换// 如果是一个执行流那么不可能同时执行2个死循环int count 0;while(1){cout new thread is created! name: name endl;sleep(1);count;if(count 5){int* p nullptr;*p 10;//故意写一个解引用空指针我们知道是会报段错误}}
}int main()
{pthread_t thread;pthread_create(thread, nullptr, start_routime, (void*)thread new);while(1){cout new thread is created! name: main endl;sleep(1);}return 0;
}命令行报错
[yyqVM-8-13-centos 2023_03_18_multiThread]$ ./mythread
new thread is created! name: main
new thread is created! name: thread new
new thread is created! name: main
new thread is created! name: thread new
new thread is created! name: main
new thread is created! name: thread new
new thread is created! name: main
new thread is created! name: thread new
new thread is created! name: main
new thread is created! name: thread new
new thread is created! name: main
Segmentation fault由此可以看出当一个线程出异常了会直接影响其他线程的正常运行。因为信号是整体发送给进程的本质是将信号发给对应进程的pid而一个进程的所有线程pid值是相同的就会给每个线程的PCB里写入相同的信号接收到信号后所有的进程就退了。
换个角度来说每个线程所依赖的资源是进程给的当一个线程出异常进程会收到退出信号后OS回收资源是回收整个进程的资源而其他线程的资源是进程给的故所有的线程就会全部退出。
以上是从信号异常资源的视角来看待线程健壮性的问题。
POSIX线程库的errno
我们学习的是POSIX线程库有以下特征
1、与线程有关的函数构成了一个完整的系列绝大多数函数的名字都是pthread_打头的
2、要使用这些函数库要通过引入头文pthread.h
3、链接这些线程函数库时要使用编译器命令的-l pthread选项。
用户级线程库的pthread这一类函数出错时不会设置全局变量errno虽然大部分其他POSIX函数会这样做而是将错误代码通过返回值返回。因为线程是共享一份资源的如果多个线程对同一个全局变量进行访问errno也是全局变量就会因为缺乏访问控制而带来一些问题因此对于pthreads函数的错误建议通过返回值来判定。
简单了解clone
允许用户创建一个进程/轻量级进程fork()/vfork()就是调用clone来实现的。
#include sched.h
功能创建一个进程/轻量级进程原型int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, .../* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );参数child_stack:子栈(用户栈)flags:
返回值创建失败返回-1创建成功返回线程ID/* Prototype for the raw system call */long clone(unsigned long flags, void *child_stack, void *ptid, void *ctid, struct pt_regs *regs);创建多线程
#include iostream
#include string
#include vector
#include pthread.h
#include unistd.husing namespace std;void* start_routime(void* args)
{string name static_castconst char*(args);//安全地进行强制类型转换while(1){cout new thread is created! name: name endl;sleep(1);}
}int main()
{vectorpthread_t threadIDs (10, pthread_t());for(size_t i 0; i 10; i){pthread_t tid;char nameBuffer[64];snprintf(nameBuffer, sizeof(nameBuffer), %s : %d, thread, i);pthread_create(tid, nullptr, start_routime, (void*)nameBuffer);//是缓冲区的起始地址无法保证创建的新线程允许先后次序threadIDs[i] tid;sleep(1);}for(auto e : threadIDs){cout e endl;}while(1){cout new thread is created! name: main endl;sleep(1);}return 0;
}现象当创建多个线程的循环中没有添加sleep语句时我们可能看到的输出一直是某个线程。 分析当我们创建新的线程时每个线程是独立的执行流。首先创建多个新线程谁先运行是不确定的其次因为nameBuffer是被所有线程共享的主线程是把缓冲区的起始地址传给每个线程在循环里nameBuffer在一直被主进程更新所以每个进程能拿到的都是被主进程更新后的最新的进程id。
多线程数据私有
所以如果我们想让各个线程独立执行代码这样的写法是不对的那如何给线程传递正确的结构呢既然nameBuffer是同一个变量一样的地址那就每次传入不同的地址呀
//当成结构体使用
class ThreadData
{
public:pthread_t tid;char nameBuffer[64];
};
//对应的操作函数如下
void* start_routime(void* args)//args传递的时候也是拷贝了一份地址传过去。不管是传值传参还是传引用传参都会发生拷贝
{ThreadData* td static_castThreadData*(args);//安全地进行强制类型转换int cnt 10;while(cnt){cout new thread is created! name: td-nameBuffer 循环次数cnt: cnt endl;cnt--;sleep(1);}delete td;return nullptr;
}
int main()
{vectorThreadData* threads;for(size_t i 0; i 10; i){// 此处 td是指针传给每个线程的td指针都是不一样的实现数据私有ThreadData* td new ThreadData();snprintf(td-nameBuffer, sizeof(td-nameBuffer), %s : %d, thread, i 1);pthread_create(td-tid, nullptr, start_routime, (void*)td);threads.push_back(td);}for(auto e : threads){cout create thread name: e-nameBuffer tid: e-tid endl;}int cnt 7;while(cnt){cout new thread is created! name: main endl;cnt--;sleep(1);}return 0;
}通过传new出来的结构体指针实现多线程数据私有
重入状态
start_routime()函数同时被10个线程访问在程序运行期间处于重入状态。站在变量的角度由于函数没有访问全局变量访问的都是局部变量故是可重入函数。【严格来说这不算可重入函数因为cout是访问文件的而我们只有一个显示器在输出到显示器的时候有可能会出错】
对全局变量进行原子操作的是可重入函数。
独立栈空间
每个线程都有自己独立的栈空间
线程ID
线程id是它独立栈空间的起始地址
线程等待pthread_join
**join是阻塞式等待。**线程也是要被等待的如果不等待会造成类似僵尸进程的问题–内存泄漏。作用1、获取线程退出信息2、回收线程资源。但与进程不同的是线程不用获取退出信号因为一旦线程出异常收到信号了整个进程都会退出。
pthread_join不考虑异常问题线程出异常了进程直接来处理。
start_routime返回值的类型是void*pthread_join()中retval参数的类型是void**。两者之间有关联。
#include pthread.h
int pthread_join(pthread_t thread, void **retval);
参数thread:线程idretval输出型参数:用于获取线程函数结束时返回的退出结果
返回值成功返回0失败返回错误码具体使用
void* retval nullptr;//相当于把start_routime返回的指针这里的指针是指针地址是个字面值存到ret这里的ret是指针变量里面去
int n pthread_join(tid, retval);
assert(n 0);线程终止时可以返回一个指针比如堆空间的地址、对象的地址等并可以被主线程取到由此可以完成信息交互。
例如进程有阻塞式等待和非阻塞式等待用信号捕捉函数设置成signal(SIGCHLD, SIG_IGN);就可而进程没有非阻塞式等待。
线程分离pthread_detach
默认情况下我们创建的新线程都是joinable的线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成系统泄漏如果不关心线程的返回值join是一种负担这个时候我们可以告诉系统当线程退出时自动释放线程资源。
功能分离线程与joinable是互斥的
#include pthread.h
原型int pthread_detach(pthread_t thread);
返回值成功返回0失败返回错误码但不设置错误码不被设置到errno
//使用1线程自己分离自己
pthread_detach(pthread_self());
//使用2主线程分离其他线程当线程自己分离自己后主线程再调用pthread_join()【需要主动让主进程的join后与detach执行】此时jion函数会返回22表示Invalid argument
为什么要先让detach执行因为新线程和主线程谁先执行是不确定的当新线程去执行自己的任务时假设新线程还没来得及执行detach而主线程就已经join了那么detach就无效了。
功能获取调用该函数的线程ID
#include pthread.h
pthread_t pthread_self(void);线程终止
线程退出return/pthread_exit return nullptr; return返回就表示该线程终止。 pthread_exit(nullptr); 线程退出的专用pthread_exit()函数。 #include pthread.h void pthread_exit(void *retval);
exit用于终止进程不能用于终止线程。任何一个执行流调用exit都会让整个进程退出。
发现了没return和pthread_exit都有个nullptr参数这个返回值会放在pthread库里面的。
后续线程等待时就是到pthread库里取到这个值。
线程取消pthread_cancel
线程被取消的前提是线程已经在运行了由主线程给对应的线程发送取消命令。收到的退出码retval是-1-1实际上是宏#define PTHREAD_CANCELED ((void*) -1)。
原生线程库pthread
站在上层的角度从语言层面来看原生线程库
在linux上任何语言如果要实现多线程必定要用到pthread库如何看待C11中的多线程呢C11中的多线程在linux环境下本质是对pthread库的封装。
#include iostream
#include thread
#include unistd.h
using namespace std;void thread_run()
{int cnt 5;while(cnt){cout 我是新线程 endl;sleep(1);}
}int main()
{thread t1(thread_run);while(true){cout 我是主线程 endl;}t1.join();return 0;
}
//这份代码用g编译如果不带-lpthread选项就会报错说明C就是封装了原生线程库用原生线程库写出来的代码是不可跨平台的但是效率更高用C写出来的多平台通用但是效率偏低。 原生线程库是共享库可以同时被多个用户使用。那么如何对用户创建出来的线程做管理呢 linux给出的解决方案是让原生线程库采用一定的方法对用户创建的线程做管理只不过在库里需要添加的线程属性比较少包括线程id等会存在一个union pthread_attr_t{};结构体里然后与内核中的轻量级进程一一对应内核提供线程执行流的调度。linux用户级线程内核轻量级进程11。 线程局部存储__thread
全局变量保存在进程地址空间的已初始化数据区的被__thread修饰的全局变量保存在线程局部存储中共享区的线程结构体里。这个是线程独有的介于全局变量和局部变量之间的一种存储方案。 文章转载自: http://www.morning.dxzcr.cn.gov.cn.dxzcr.cn http://www.morning.wjrtg.cn.gov.cn.wjrtg.cn http://www.morning.qytby.cn.gov.cn.qytby.cn http://www.morning.thnpj.cn.gov.cn.thnpj.cn http://www.morning.bnfsw.cn.gov.cn.bnfsw.cn http://www.morning.rqmr.cn.gov.cn.rqmr.cn http://www.morning.dxpqd.cn.gov.cn.dxpqd.cn http://www.morning.yqsq.cn.gov.cn.yqsq.cn http://www.morning.mbprq.cn.gov.cn.mbprq.cn http://www.morning.ffgbq.cn.gov.cn.ffgbq.cn http://www.morning.ghssm.cn.gov.cn.ghssm.cn http://www.morning.qdsmile.cn.gov.cn.qdsmile.cn http://www.morning.ggcjf.cn.gov.cn.ggcjf.cn http://www.morning.drcnf.cn.gov.cn.drcnf.cn http://www.morning.gbsby.cn.gov.cn.gbsby.cn http://www.morning.ksqzd.cn.gov.cn.ksqzd.cn http://www.morning.mrlls.cn.gov.cn.mrlls.cn http://www.morning.qymqh.cn.gov.cn.qymqh.cn http://www.morning.nmngg.cn.gov.cn.nmngg.cn http://www.morning.lqtwb.cn.gov.cn.lqtwb.cn http://www.morning.ddgl.com.cn.gov.cn.ddgl.com.cn http://www.morning.sthgm.cn.gov.cn.sthgm.cn http://www.morning.nlgnk.cn.gov.cn.nlgnk.cn http://www.morning.hjjkz.cn.gov.cn.hjjkz.cn http://www.morning.yktr.cn.gov.cn.yktr.cn http://www.morning.hjwzpt.com.gov.cn.hjwzpt.com http://www.morning.rzbgn.cn.gov.cn.rzbgn.cn http://www.morning.rhdln.cn.gov.cn.rhdln.cn http://www.morning.kxryg.cn.gov.cn.kxryg.cn http://www.morning.ysnbq.cn.gov.cn.ysnbq.cn http://www.morning.gryzk.cn.gov.cn.gryzk.cn http://www.morning.tgtrk.cn.gov.cn.tgtrk.cn http://www.morning.wbns.cn.gov.cn.wbns.cn http://www.morning.fqyqm.cn.gov.cn.fqyqm.cn http://www.morning.mwbqk.cn.gov.cn.mwbqk.cn http://www.morning.fwllb.cn.gov.cn.fwllb.cn http://www.morning.qsctt.cn.gov.cn.qsctt.cn http://www.morning.nkiqixr.cn.gov.cn.nkiqixr.cn http://www.morning.nrftd.cn.gov.cn.nrftd.cn http://www.morning.qnzld.cn.gov.cn.qnzld.cn http://www.morning.zcxjg.cn.gov.cn.zcxjg.cn http://www.morning.qkskm.cn.gov.cn.qkskm.cn http://www.morning.npmpn.cn.gov.cn.npmpn.cn http://www.morning.lktjj.cn.gov.cn.lktjj.cn http://www.morning.dnqlba.cn.gov.cn.dnqlba.cn http://www.morning.mxptg.cn.gov.cn.mxptg.cn http://www.morning.gmjkn.cn.gov.cn.gmjkn.cn http://www.morning.pxwjp.cn.gov.cn.pxwjp.cn http://www.morning.qqxmj.cn.gov.cn.qqxmj.cn http://www.morning.wqbrg.cn.gov.cn.wqbrg.cn http://www.morning.nzhzt.cn.gov.cn.nzhzt.cn http://www.morning.jxwhr.cn.gov.cn.jxwhr.cn http://www.morning.mjats.com.gov.cn.mjats.com http://www.morning.jhrkm.cn.gov.cn.jhrkm.cn http://www.morning.dygsz.cn.gov.cn.dygsz.cn http://www.morning.rtbx.cn.gov.cn.rtbx.cn http://www.morning.duqianw.com.gov.cn.duqianw.com http://www.morning.hwhnx.cn.gov.cn.hwhnx.cn http://www.morning.rlzxr.cn.gov.cn.rlzxr.cn http://www.morning.rlwcs.cn.gov.cn.rlwcs.cn http://www.morning.kjfqf.cn.gov.cn.kjfqf.cn http://www.morning.ksqyj.cn.gov.cn.ksqyj.cn http://www.morning.bwqcx.cn.gov.cn.bwqcx.cn http://www.morning.nstml.cn.gov.cn.nstml.cn http://www.morning.zrks.cn.gov.cn.zrks.cn http://www.morning.zfwjh.cn.gov.cn.zfwjh.cn http://www.morning.bfgpn.cn.gov.cn.bfgpn.cn http://www.morning.dhyzr.cn.gov.cn.dhyzr.cn http://www.morning.pqbkk.cn.gov.cn.pqbkk.cn http://www.morning.wnhml.cn.gov.cn.wnhml.cn http://www.morning.hytqt.cn.gov.cn.hytqt.cn http://www.morning.prhqn.cn.gov.cn.prhqn.cn http://www.morning.nslwj.cn.gov.cn.nslwj.cn http://www.morning.sjzsjsm.com.gov.cn.sjzsjsm.com http://www.morning.jyzqn.cn.gov.cn.jyzqn.cn http://www.morning.nrfrd.cn.gov.cn.nrfrd.cn http://www.morning.tsnq.cn.gov.cn.tsnq.cn http://www.morning.fthcn.cn.gov.cn.fthcn.cn http://www.morning.mjmtm.cn.gov.cn.mjmtm.cn http://www.morning.kwhrq.cn.gov.cn.kwhrq.cn