5网站建设,山西山西省建设厅网站,南网站建设 首选搜点网络,wordpress文章链接设置1.线程特性
1.1线程优点 创建一个新线程的代价要比创建一个新进程小得多与进程之间的切换相比#xff0c;线程之间的切换需要操作系统做的工作要少很多线程占用的资源要比进程少很多能充分利用多处理器的可并行数量在等待慢速I/O操作结束的同时#xff0c;程序可执行其他的计…1.线程特性
1.1线程优点 创建一个新线程的代价要比创建一个新进程小得多与进程之间的切换相比线程之间的切换需要操作系统做的工作要少很多线程占用的资源要比进程少很多能充分利用多处理器的可并行数量在等待慢速I/O操作结束的同时程序可执行其他的计算任务计算密集型应用为了能在多处理器系统上运行将计算分解到多个线程中实现I/O密集型应用为了提高性能将I/O操作重叠。线程可以同时等待不同的I/O操作。 1.2线程缺点 性能损失 一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多那么可能会有较大的性能损失这里的性能损失指的是增加了额外的同步和调度开销而可用的资源不变。健壮性降低 编写多线程需要更全面更深入的考虑在一个多线程程序里因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的换句话说线程之间是缺乏保护的。缺乏访问控制 进程是访问控制的基本粒度在一个线程中调用某些OS函数会对整个进程造成影响。编程难度提高 编写与调试一个多线程程序比单线程程序困难得多。 1.3线程异常 单个线程如果出现除零野指针问题导致线程崩溃进程也会随着崩溃线程是进程的执行分支线程出异常就类似进程出异常进而触发信号机制终止进程进程终止该进程内的所有线程也就随即退出。 1.4线程用途 合理的使用多线程能提高CPU密集型程序的执行效率合理的使用多线程能提高IO密集型程序的用户体验如生活中我们一边写代码一边下载开发工具就是多线程运行的一种表现 2.编写代码理解多线程
首先我们创建一个Makefile编写如下代码
mythread:mythread.ccg -o $ $^ -stdc11.PHONY:clean
clean:rm -rf mythread
然后创建一个mythread.cc的c源文件编写如下代码
#includeiostream
#includepthread.h
#includeunistd.hvoid * ThreadRoutine(void * arg)
{while(true){std::coutI am a new pthreadstd::endl;sleep(1);}
}int main()
{pthread_t tid;pthread_create(tid,nullptr,ThreadRoutine,nullptr);while(true){std::coutI am a main pthreadstd::endl;sleep(1);}return 0;
}
然后编译运行发现报错了 g说不认识这个pthread_create这个接口那么我们就需要来谈一个话题了Linux有没有真正的线程呢没有内核中只有轻量级进程的概念所以Linux操作系统只会提供轻量级进程创建的系统调用不会直接提供线程创建的接口。 因为可能用户学习的操作系统中是有线程这个概念的但是Linux内核只认轻量级进程LWP所以两者就无法达成共识为了让用户认为自己创建的是线程然后Linux操作系统认为创建的是轻量级进程所以就有了中间的软件层pthread原生线程库这个库一般都是跟linux配套在一起的所以不用担心用户因为没有这个pthread原生线程库而调用创建线程的接口而失败。但是又有人说了为什么Linux非得用轻量级进程而不去实现线程因为轻量级进程又不得不实现一个像pthread原生线程库这样的库这不是多此一举吗其实并不是这样的用轻量级进程LWP模拟线程本就是Linux操作系统的一大亮点而且中间有一层pthread原生线程库反而可以让接口层与实现层进行解耦未来如果pthread原生线程库要是更新了也不会影响到Linux内核比如说无论上层怎么更新同样可以是用同一版本的Linux内核这样维护性的效率就大大提高了这样的实现更符合软件工程。这个库我们是可以通过搜索该路径看到的 所以上述代码报错的原因是找不到我们对应的库所以我们将Makefile中的代码改为
mythread:mythread.ccg -o $ $^ -stdc11 -lpthread.PHONY:clean
clean:rm -rf mythread
运行结果 我们可以查看一下链接的库: 此时两个线程就开始运行了。
下面我们对线程创建再进行扩展
比如说我们如何给线程传参呢如何传递线程创建的时间啊执行的任务线程名称
#includeiostream
#includepthread.h
#includeunistd.h
#includestring
#includefunctional
#includetime.husing func_t std::functionvoid();class ThreadData
{
public:ThreadData(const std::string name,const uint64_t ctime,func_t f):threadname(name),createtime(ctime),func(f){}
public:std::string threadname;uint64_t createtime;func_t func;
};void Print()
{std::cout我是线程执行的大任务的一部分std::endl;
}void * ThreadRoutine(void * args)
{ThreadData *td static_castThreadData*(args);while(true){std::coutI am a new pthreadthreadname: td-threadnamecreate time: td-createtimestd::endl;td-func();sleep(1);}
}int main()
{pthread_t tid;ThreadData * td new ThreadData(thread 1,(uint64_t)time(nullptr),Print);pthread_create(tid,nullptr,ThreadRoutine,td);while(true){std::coutI am a main pthreadstd::endl;sleep(1);}return 0;
} 运行结果 下面如何修改代码变成创建多线程呢
#includeiostream
#includepthread.h
#includeunistd.h
#includestring
#includefunctional
#includetime.h
#includevectorconst int threadnum 5;
using func_t std::functionvoid();class ThreadData
{
public:ThreadData(const std::string name,const uint64_t ctime,func_t f):threadname(name),createtime(ctime),func(f){}
public:std::string threadname;uint64_t createtime;func_t func;
};void Print()
{std::cout我是线程执行的大任务的一部分std::endl;
}void * ThreadRoutine(void * args)
{ThreadData *td static_castThreadData*(args);while(true){std::coutI am a new pthreadthreadname: td-threadnamecreate time: td-createtimestd::endl;td-func();sleep(1);}
}int main()
{std::vectorpthread_t pthreads;for(int i 0;ithreadnum;i){pthread_t tid;char threadname[64];snprintf(threadname,sizeof(threadname),%s - %lu,thread,i1);ThreadData * td new ThreadData(threadname,(uint64_t)time(nullptr),Print);pthread_create(tid,nullptr,ThreadRoutine,td);pthreads.push_back(tid);sleep(1);}while(true){std::coutI am a main pthreadstd::endl;sleep(1);}return 0;
}
把创建线程的代码放入for循环然后将threaname都有不同的线程名称而且将tid保存起来我们用到了vector.
下面我们运行一下 所以这里我们就创建5个新线程。
下面我们来研究两个问题
1.线程的健壮性问题当一个进程有多个线程时只要有一个线程触发了异常整个进程也会受到相应的影响。
比如说我们修改一个函数中的代码故意制造除零错误触发段错误来进行验证
void * ThreadRoutine(void * args)
{int a 10;ThreadData *td static_castThreadData*(args);while(true){std::coutI am a new pthreadthreadname: td-threadnamecreate time: td-createtimestd::endl;td-func();if(td-threadnamethread - 4){std::couttd-threadname 触发了异常std::endl;a/0;}sleep(1);}
}
当线程名称为第四个的时候让其触发段错误促使让操作系统发送8号信号让进程终止。 我们通过监视窗口可以看到进程直接被终止了说明一旦线程出现了异常那么操作系统会给进程发信号让进程退出那么进程都退了线程自然也就退了。 2.观察一下thread id
#includeiostream
#includepthread.h
#includeunistd.h
#includestring
#includefunctional
#includetime.h
#includevectorconst int threadnum 5;
using func_t std::functionvoid();class ThreadData
{
public:ThreadData(const std::string name,const uint64_t ctime,func_t f):threadname(name),createtime(ctime),func(f){}
public:std::string threadname;uint64_t createtime;func_t func;
};void Print()
{std::cout我是线程执行的大任务的一部分std::endl;
}void * ThreadRoutine(void * args)
{int a 10;ThreadData *td static_castThreadData*(args);while(true){std::coutI am a new pthreadthreadname: td-threadnamecreate time: td-createtimestd::endl;td-func();// if(td-threadnamethread - 4)// {// std::couttd-threadname 触发了异常std::endl;// a/0;// }sleep(1);}
}int main()
{std::vectorpthread_t pthreads;for(int i 0;ithreadnum;i){pthread_t tid;char threadname[64];snprintf(threadname,sizeof(threadname),%s - %lu,thread,i1);ThreadData * td new ThreadData(threadname,(uint64_t)time(nullptr),Print);pthread_create(tid,nullptr,ThreadRoutine,td);pthreads.push_back(tid);sleep(1);}for(const auto tid : pthreads){std::coutthread id : tidstd::endl;}while(true){std::coutI am a main pthreadstd::endl;sleep(1);}return 0;
}
在上述代码基础上添加一段打印tid的代码运行结果 那么这些thread id这么长的一段数字到底是什么意思呢为了更清晰的理解这串数字我们可以将其16进制打印出来。
我们再来认识一个线程获取自身的id的一个接口 然后再写一段代码
#includeiostream
#includepthread.h
#includeunistd.h
#includestring
#includefunctional
#includetime.h
#includevectorstd::string toHex(pthread_t tid)
{char id[64];snprintf(id,sizeof(id),0x%lx,tid);return id;
}void * threadRoutine(void * arg)
{usleep(1000);std::string name static_castconst char *(arg);while(true){std::coutnew thread is running, thread name: name thread id : toHex(pthread_self())std::endl;sleep(1); }
}int main()
{pthread_t tid;pthread_create(tid,nullptr,threadRoutine,(void *)thread-1);while(true){std::coutmain thread, sub thread: tid main thread id : toHex(pthread_self())std::endl;sleep(1);}return 0;
}
运行结果 数字有这么长一串是因为我用的是64位系统的然后这更像是一个地址其实thread id的本质就是一个地址。
线程既然可以创建那么我们如何把它终止呢我们继续来认识一个线程终止的接口 需要通过传一个指针让线程终止是不是呢我们通过将新线程运行的函数修改成如下
void * threadRoutine(void * arg)
{usleep(1000);std::string name static_castconst char *(arg);int cnt 5;while(cnt--){std::coutnew thread is running, thread name: name thread id : toHex(pthread_self())std::endl;sleep(1); }//return nullptr;pthread_exit(nullptr);
}
运行结果 我们发现运行到后面就剩一个主线程了说明新线程退出了。
下面我们来谈谈关于线程返回值的问题
1.我们要获取返回值该如何获取呢
2.线程在本质上其实就是Linux操作系统下的轻量级进程那么当轻量级进程终止了它的PCB会不会立即释放呢
3.线程默认要被等待吗是的线程退出没有等待会导致类似进程的僵尸问题。线程退出时如何获取新线程的返回值呢 首先我们先认识一个接口 参数是thread id 和 一个二级指针只要等待成功了返回值就是0.
下面我们用这个线程等待的接口来进行测试
#includeiostream
#includepthread.h
#includeunistd.h
#includestring
#includefunctional
#includetime.h
#includevectorstd::string toHex(pthread_t tid)
{char id[64];snprintf(id,sizeof(id),0x%lx,tid);return id;
}void * threadRoutine(void * arg)
{usleep(1000);std::string name static_castconst char *(arg);int cnt 5;while(cnt--){std::coutnew thread is running, thread name: name thread id : toHex(pthread_self())std::endl;sleep(1); }return nullptr;// pthread_exit(nullptr);
}int main()
{pthread_t tid;pthread_create(tid,nullptr,threadRoutine,(void *)thread-1);std::coutmain thread, main thread id : toHex(pthread_self())std::endl;sleep(10);int n pthread_join(tid,nullptr);std::coutmain thread donen : nstd::endl;sleep(5);return 0;
} 运行结果 我们看到最后的返回值是0所以表示等待成功了。 如果我们要得到新线程的返回值那么我们得到的也应该是void *,所以为了得到一个void*就需要传入一个void * * 。
那么下面我们来修改一下代码来证明一下
#includeiostream
#includepthread.h
#includeunistd.h
#includestring
#includefunctional
#includetime.h
#includevectorstd::string toHex(pthread_t tid)
{char id[64];snprintf(id,sizeof(id),0x%lx,tid);return id;
}void * threadRoutine(void * arg)
{usleep(1000);std::string name static_castconst char *(arg);int cnt 5;while(cnt--){std::coutnew thread is running, thread name: name thread id : toHex(pthread_self())std::endl;sleep(1); }return (void*)thread-1 done;// return nullptr;// pthread_exit(nullptr);
}int main()
{pthread_t tid;pthread_create(tid,nullptr,threadRoutine,(void *)thread-1);std::coutmain thread, main thread id : toHex(pthread_self())std::endl;void * ret nullptr;int n pthread_join(tid,ret);std::coutmain thread done n : nstd::endl;std::coutmain get new thread return : (const char *)retstd::endl;return 0;
}运行结果 果然获取到了对应的返回值。
线程是可以被设置为分离状态的(可以理解为 该线程不受主线程的管控了)线程默认情况下是joinable的.
下面我们用代码来实现线程设置为分离状态出现的现象也就是主线程等待新线程的返回值不成功
#includeiostream
#includepthread.h
#includeunistd.h
#includestring
#includefunctional
#includetime.h
#includevectorstd::string toHex(pthread_t tid)
{char id[64];snprintf(id,sizeof(id),0x%lx,tid);return id;
}void * threadRoutine(void * arg)
{pthread_detach(pthread_self());// usleep(1000);// std::string name static_castconst char *(arg);int cnt 5;while(cnt--){std::coutthread is running...std::endl;sleep(1); }// return (void*)thread-1 done;return nullptr;// pthread_exit(nullptr);
}int main()
{pthread_t tid;pthread_create(tid,nullptr,threadRoutine,(void *)thread-1);sleep(1);// std::coutmain thread, main thread id : toHex(pthread_self())std::endl;int n pthread_join(tid,nullptr);std::coutmain thread done n : nstd::endl;return 0;
}
运行结果 我们发现返回值n不在是0了说明等待失败了。
当然也可以这样分离
int main()
{pthread_t tid;pthread_create(tid,nullptr,threadRoutine,(void *)thread-1);sleep(1);pthread_detach(tid);// std::coutmain thread, main thread id : toHex(pthread_self())std::endl;int n pthread_join(tid,nullptr);std::coutmain thread done n : nstd::endl;return 0;
}
把pthread_detach()这段代码放到这个位置用主线程与新线程分离。
如果不分离运行结果是这样的 其实我们还有一种可以让线程退出的方式那就是线程取消掉用到接口pthread_cancel 下面我们运用这个接口来进行测试
#includeiostream
#includepthread.h
#includeunistd.h
#includestring
#includefunctional
#includetime.h
#includevectorstd::string toHex(pthread_t tid)
{char id[64];snprintf(id,sizeof(id),0x%lx,tid);return id;
}void * threadRoutine(void * arg)
{//pthread_detach(pthread_self());// usleep(1000);// std::string name static_castconst char *(arg);int cnt 5;while(cnt--){std::coutthread is running...std::endl;sleep(1); }// return (void*)thread-1 done;return nullptr;// pthread_exit(nullptr);
}int main()
{pthread_t tid;pthread_create(tid,nullptr,threadRoutine,(void *)thread-1);sleep(5);//pthread_detach(tid);// std::coutmain thread, main thread id : toHex(pthread_self())std::endl;int n pthread_cancel(tid);std::coutmain thread cancel done, n : nstd::endl;void * ret nullptr;n pthread_join(tid,ret);std::coutmain thread join done, n : nthread return : (int64_t)retstd::endl;return 0;
}
运行结果 说明我们线程取消成功了同时被join了退出码是-1.
我们把线程分离加上去 int main()
{pthread_t tid;pthread_create(tid,nullptr,threadRoutine,(void *)thread-1);sleep(5);pthread_detach(tid);// std::coutmain thread, main thread id : toHex(pthread_self())std::endl;int n pthread_cancel(tid);std::coutmain thread cancel done, n : nstd::endl;void * ret nullptr;n pthread_join(tid,ret);std::coutmain thread join done, n : nthread return : (int64_t)retstd::endl;return 0;
} 运行结果就是 线程被分离了也是可以取消的但是不能被join.