Linux ---线程控制(二) 创建删除一个新线程的代价要比创建一个新进程小得多。切换与进程之间的切换丢弃cache重新进行命中缓存相比线程之间的切换不需要对cache进行切换需要操作系统做的工作要少很多。一、 创建线程pthread_create 创建一个POSIX 线程Linux 下常用轻量级线程操作系统调度新线程执行指定入口函数主线程与新线程并发运行。参数说明pthread_t *thread指针函数成功后会把新线程的唯一 ID 写入该变量后续pthread_join/pthread_detach靠此 ID 操作线程。pthread_attr_t *attr线程属性结构体栈大小、分离状态、调度优先级等传NULL采用系统默认配置。void *(*start_routine)(void *)线程执行函数格式固定接收一个 void参数返回 void。void *arg传递给线程函数的唯一参数多参数可封装到结构体再传结构体指针。#include iostream #include cstdio #include string #include pthread.h #include unistd.h void *Routine(void *args) { std::string name static_castconst char *(args); printf(--------------------------------------------------\n); while (true) { // std::cout我是新线程namestd::endl; printf(我是新线程%s,tid:0x%lx,pid:%d\n, name.c_str(), pthread_self(), getpid()); sleep(1); } } int main() { int n; const int num 10; for (int i 0; i num; i) { pthread_t tid; char threadname[64]; snprintf(threadname, sizeof(threadname), thread-%d, i 1); pthread_create(tid, nullptr, Routine, threadname); (void)n; usleep(1); } // pthread_t tid; // int npthread_create(tid,nullptr,Routine,(void*)thread-1); // (void)n; // printf(new thread id :0x%lx\n,tid); // while(true) // { // std::cout我是主线程....std::endl; // sleep(1); //} while (true) { // std::cout我是主线程....std::endl; printf(我是主线程tid:0x%lx\n, pthread_self()); sleep(1); } return 0; }二、线程终止如果需要只终止某个线程而不终止整个进程,可以有三种方法:1. 从线程函数return。这种方法对主线程不适⽤,从main函数return相当于调用exit。2. 线程可以调用pthread_ exit终止自己。3. ⼀个线程可以调用pthread_ cancel终止同⼀进程中的另一个线程。三、线程等待pthread_join你的代码不加它会发生什么你现在 main 创建线程后直接结束main 函数执行完毕 → 进程直接退出OS 会强制杀死所有子线程子线程循环打印大概率只会输出一两行甚至完全不输出加上pthread_join(tid, nullptr);后 主线程停在这里死等子线程子线程循环跑不会被提前杀掉调用pthread_join的线程会阻塞挂起直到参数中指定 ID 的目标线程运行终止。 目标线程的终止方式不同pthread_join通过第二个参数value_ptr传出的终止状态也不一样分四种情况目标线程通过return正常返回value_ptr指向的内存中保存线程函数return的返回值。目标线程被其他线程调用pthread_cancel取消终止value_ptr指向的内存中存入常量PTHREAD_CANCELED。目标线程自身调用pthread_exit主动退出value_ptr指向的内存中存放传入pthread_exit的参数。不需要获取线程终止状态直接给value_ptr传入NULL即可。#include iostream #include cstdio #include string #include pthread.h #include unistd.h class Res { public: int code; std::string name; std::string info; }; void *Routine(void *args) { std::string name static_castconst char *(args); while (true) { std::cout new thread std::endl; sleep(1); break; } Res *res new Res(); res-code 10; res-name name; res-info 我这个线程以及完蛋了你们继续努力; return (void *)res; } int main() { pthread_t tid; pthread_create(tid, nullptr, Routine, (void *)thread-1); sleep(10); // 1、一般而言必须等待新线程退出如果不等待就会形成类似僵尸问题 // 2、为了获取新线程执行的结果 void *retval nullptr; // 新线程出异常进程就会全部退出没机会join成功不需要关心异常 int n pthread_join(tid, (void **)retval); // 只关心正常情况 if (n 0) { Res *p(Res*)retval; std::cout join success p-code std::endl; std::cout join success p-name std::endl; std::cout join success p-info std::endl; delete p; } return 0; }四、分离线程默认 情况下新创建的线程是joinable的。线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成系统泄露如果不关心线程的返回值join是一种负担这个时候我们可以告诉系统当线程退出时自动释放线程资源。线程被分离线程创建后默认是joinable可汇合状态代表这个线程退出后内核会保留它的资源、退出码必须通过pthread_join()回收否则会变成僵尸线程。等待线程是阻塞等待的想让线程全部自动结束掉主进程不想等待把目标被等待的线程设置为分离状态。一旦线程分离后主线程就不管了。 分离是一种状态。pthread_detach返回值#include iostream #include cstdio #include string #include pthread.h #include unistd.h class Res { public: int code; std::string name; std::string info; }; void *Routine(void *args) { int cnt5; while(cnt) { std::cout新线程在运行cnt--std::endl; sleep(1); } return nullptr; } int main() { pthread_t tid; pthread_create(tid, nullptr, Routine, (void *)thread-1); std::coutmain thread 分离线程std::endl; pthread_detach(tid); sleep(10); }执行结果是线程ID的理解及进程地址空间布局线程局部存储#include iostream #include cstdio #include string #include pthread.h #include unistd.h __thread int id0; void *routine(void *args) { std::string name static_castconst char *(args); while (true) { std::cout new thread id : id std::endl; id; sleep(1); } return nullptr; } int main() { pthread_t tid; pthread_create(tid, nullptr, routine, (void *)thread-1); while (true) { std::cout main thread id : id std::endl; sleep(1); } pthread_join(tid, nullptr); return 0; }运行结果是从这里可以看到在同一个全局变量内核会给每一个创建出来的线程各自分配一份独立的变量副本。子线程操作的是自己专属的id主线程操作的是自己的专属id两份内存互相不占用、互相不修改。要让线程局部存储1、加__thread(两个_)只能修饰全局变量或者函数内static静态变量2、变量每个线程各自一份副本互相完全隔离3、声明格式__thread 数据类型 变量名初始值