当前位置: 首页 > news >正文

网站优化需求午夜做网站

网站优化需求,午夜做网站,自适应网站建设需要注意什么,福田做网站公司怎么选线程概念 在一个程序里的一个执行路线就叫做线程#xff08;thread#xff09;。更准确的定义是#xff1a;线程是“一个进程内部的控制序列”。一切进程至少都有一个执行线程。线程在进程内部运行#xff0c;本质是在进程地址空间内运行。在Linux系统中#xff0c;在CPU眼…线程概念 在一个程序里的一个执行路线就叫做线程thread。更准确的定义是线程是“一个进程内部的控制序列”。一切进程至少都有一个执行线程。线程在进程内部运行本质是在进程地址空间内运行。在Linux系统中在CPU眼中看到的PCB都要比传统的进程更加轻量化。透过进程虚拟地址空间可以看到进程的大部分资源将进程资源合理分配给每个执行流就形成了线程执行流。 Linux内核中没有真正意义的线程。Linux是用进程PCB来模拟线程的是一种完全属于自己的一套线程方案。站在CPU的视角每一个PCB都可以称之为轻量级进程。进程是用来整体申请资源的而线程是用来向进程要资源的。线程是CPU调度的基本单位进程是承担分配系统资源的实体。 使用进程PCB来模拟线程这样做的好处是什么 简单维护成本大大降低可靠高效。不需要再为线程创建对应的数据结构和算法。 但是OS只认线程用户程序员也只认线程而Linux无法直接提供创建线程的系统调用接口只能给我们提供创建轻量级进程的接口 pthread库 使用用户级线程库pthread库对上用户提供对线程操作相关接口对下操作系统访问底层相关接口将对线程的操作转换为对轻量级进程的相关操作任何Linux操作系统都必须默认携带这个库称之为原生线程库。使用户可以在不了解底层实现细节的情况下进行多线程编程。 pthread_create 创建新线程。 thread返回线程ID。 attr设置线程属性线程栈大小调度策略等指向pthread_attr_t 类型的结构体指针。设置为NULL表示使用线程默认属性。 start_routine函数地址指向线程启动后要执行的函数。 arg传递给start_routine的参数。由于start_routine函数的参数类型为void *所以可以传递任意类型的数据传递前转换为void *类型即可在start_routine函数内部再将其转换回原来的类型。 成功返回0失败返回错误码。 makefile: mythread.cc: #include iostream #include cassert #include pthread.h #include unistd.h #include cstdiousing namespace std;void *thread_routine(void *args) {const char *name (const char *)args;while (true){cout 我是新线程我正在运行! name: name endl;sleep(1);} }int main() {pthread_t tid;int n pthread_create(tid, nullptr, thread_routine, (void *)thread one);assert(0 n);(void)n;// 主线程while (true){cout 我是主线程我正在运行! endl;sleep(1);}return 0; } 可以看到这两个执行流PID相同说明他们是属于同一个进程的。LWPLight Weight Proces轻量级进程ID不同。既然线程是CPU调度的基本单位前提是线程要具有标识符来标定自己的唯一性。 PID和LWP相同的是主线程。PID和LWP不相同的是新线程。 CPU调度的时候是以哪一个ID为标识符表示特定的一个执行流呢 LWP。OS在内部以LWP就可以完成对进程的区分。即便只有一个单进程也有LWP也就是OS调度的时候只关心LWP。 可以看到程序依赖的动态库.so和静态库.a。 线程一旦被创建几乎所有的资源都是被线程共享的。在进程地址空间中大部分区域都是被该进程内的多个线程所共享的。 如代码段、数据段、堆、文件描述符表、信号处理方式、当前工作目录、用户ID和组ID。 mythread.cc: #include iostream #include cstdio #include cassert #include pthread.h #include unistd.husing namespace std;int g_val 0;string fun() {return 我是一个独立的方法; }// 新线程 void *thread_routine(void *args) {const char *name (const char *)args;while (true){fun();cout 我是新线程, 我正在运行! name: name : fun() : g_val g_val : g_val endl;sleep(1);} }int main() {// typedef unsigned long int pthread_t;pthread_t tid;//这个tid是什么下面讲int n pthread_create(tid, nullptr, thread_routine, (void *)thread one);assert(0 n);(void)n;// 主线程while (true){char tidbuffer[64];snprintf(tidbuffer, sizeof(tidbuffer), 0x%x, tid);cout 我是主线程, 我正在运行!, 我创建出来的线程的tid tidbuffer : g_val g_val : g_val endl;sleep(1);}return 0; } 线程也一定要有自己的私有资源什么资源是线程私有的呢 线程私有资源 1、私有上下文数据结构 线程的上下文数据结构包含了线程在执行过程中的各种状态信息这些信息是线程独有的用于记录线程当前的执行进度和状态确保线程在被调度执行时能够正确恢复之前的执行状态。如寄存器值 2、独立的栈结构 每个线程都有自己独立的栈空间这是线程执行过程中用于存储局部变量、函数调用信息等的内存区域。 3、“TCB”属性 类似与进程PCB的“TCB”用于存储线程相关属性是线程私有的。如线程ID、线程状态、调度优先级 4、信号掩码 线程可以有自己独立的信号掩码用于指定哪些信号会被阻塞。不同线程可以根据自己的需求设置不同的信号掩码以控制对信号的处理。 5、errno变量 在 C 语言编程中errno是一个全局变量用于存储最近一次系统调用或库函数调用的错误码。但在多线程环境下每个线程有自己独立的errno副本这样可以避免不同线程之间的错误码相互干扰。 线程优点 创建一个新线程的代价要比创建一个新进程小得多。与进程之间的切换相比线程之间的切换需要操作系统做的工作要少很多。线程占用的资源要比进程少很多。能充分利用多处理器的可并行数量。在等待慢速I/O操作结束的同时程序可执行其他的计算任务。计算密集型应用为了能在多处理器系统上运行将计算分解到多个线程中实现。I/O密集型应用为了提高性能将I/O操作重叠。线程可以同时等待不同的I/O操作。 线程/进程切换 “线程与进程之间的切换相比线程之间的切换需要操作系统做的工作要少很多” 进程切换需要切换 PCB、虚拟地址空间、页表、上下文。线程切换需要切换PCB、上下文。 页表就是保存在寄存器里的地址寄存器里的一个值虚拟地址空间也是PCB里的一个地址PCB切换了虚拟地址空间也就切换了。进程切换的成本好像并不高。 那么为什么说线程切换与进程切换相比需要OS做的工作少很多呢 线程切换cache不用太多的更新但是进程切换cache会全部更新。 Cache原理 cache即高速缓冲存储器。它是一种特殊的高速存储器用于减少CPU访问主存时的等待时间提高 CPU 与主存之间的数据传输效率。 局部性原理Cache 的工作基于程序访问的局部性原理该原理包括时间局部性和空间局部性。时间局部性指的是程序在一段时间内可能会多次访问同一数据比如循环中的变量空间局部性则是指程序在访问某个数据时很可能会紧接着访问其附近的数据例如数组的顺序访问。 Cache 映射Cache 将主存中的数据块按照一定的映射规则存储到 Cache 中。当 CPU 需要访问主存中的数据时首先会在 Cache 中查找如果数据存在于 Cache 中即命中则直接从 Cache 中读取大大减少了访问时间如果数据不在 Cache 中即未命中则需要从主存中读取该数据并将其所在的数据块调入 Cache以备后续可能的再次访问。 线程切换与Cache Cache 复用优势当一个进程内进行线程切换时由于同一进程内的多个线程共享进程的地址空间和大部分资源这些线程所访问的热点数据通常是相似的。所以在 Cache 中已经缓存的热点数据对于不同线程来说都可能是有用的。线程切换时不需要对 Cache 进行切换操作新线程可以直接复用 Cache 中已有的数据这大大提高了数据的访问效率。减少 Cache 缺失开销线程切换不涉及 Cache 数据的替换和重新加载降低了 Cache 缺失的概率。Cache 缺失会导致 CPU 需要从主存中读取数据这会带来较大的时间开销。线程切换时能继续使用 Cache 中的数据就避免了这种因 Cache 缺失而产生的额外开销使得系统整体性能更优。 进程切换与Cache Cache 失效问题进程拥有独立的地址空间不同进程访问的数据通常是不同的。当进行进程切换时原来进程在 Cache 中缓存的数据对于新进程来说可能是无用的这些数据会被视为失效数据。为了给新进程的缓存数据腾出空间Cache 中的原有数据可能会被替换出去。重新缓存成本高新进程开始执行后由于 Cache 中没有其所需的热点数据需要重新从主存中读取数据并缓存到 Cache 中。这个重新缓存的过程需要花费一定的时间在这段时间内CPU 可能会处于等待状态导致系统效率降低。而且当原来的进程再次被调度执行时又需要重新缓存它所需的数据进一步增加了时间开销。 线程缺点 1、性能损失 一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多那么可能会有较大的性能损失这里的性能损失指的是增加了额外的同步和调度开销而可用的资源不变。 2、健壮性降低 编写多线程需要更全面更深入的考虑在一个多线程程序里因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的换句话说线程之间是缺乏保护的。 3、缺乏访问控制 进程是访问控制的基本粒度在一个线程中调用某些OS函数会对整个进程造成影响。 4、编程难度提高 编写与调试一个多线程程序比单线程程序困难得多。 验证线程的健壮性问题: 如果一个线程出现异常了会影响其他线程吗为什么 mythread.cc: #include iostream #include string #include pthread.h #include unistd.husing namespace std;void *start_routine(void *args) {//如果一个线程异常了会影响其他线程吗string name static_castconst char *(args); // 安全的进行强制类型转换while (true){cout new thread create success, name: name endl;sleep(1);int *p nullptr;*p 0;} }int main() {pthread_t id;pthread_create(id, nullptr, start_routine, (void *)thread new);while (true){cout new thread create success, name: main thread endl;sleep(1);}return 0; } 可以看到一个线程出现异常会直接影响其他线程说明线程健壮性或者鲁棒性较差。 为什么呢信号是整体发送给进程的当当前进程发生异常时因为所有线程的PID都一样所以OS会给所有线程发送相应的信号。一个进程创建时要申请资源异常退出时要回收资源进程的资源都被释放了线程赖以生存的资源也不存在了所以其他线程也要退出。 线程异常 单个线程如果出现除零野指针问题导致线程崩溃进程也会随着崩溃 线程是进程的执行分支线程出异常就类似进程出异常进而触发信号机制终止进程进程终止该进程内的所有线程也就随即退出。 线程用途 合理的使用多线程能提高计算密集型程序如CPU、加密、解密、算法等的执行效率。比如要对一个10g的文件进行压缩单进程可能只能用一个CPU资源或者一部分CPU资源读取、压缩、写入操作是顺序执行的没有并行性。如果是多线程的话可以使用两个线程在不同的CPU核心上并行执行同时压缩一个线程压缩5g。合理的使用多线程能提高IO密集型程序如外设、访问磁盘、显示器、网络的用户体验如生活中我们一边写代码一边下载开发工具就是多线程运行的一种表现 线程创建 pthread_create fork和vfork都是创建子进程vfork创建的子进程和父进程共享地址空间其实就是轻量级进程的概念。操作系统提供的创建轻量级进程的接口是clone函数调用太复杂。用户也不使用。 接下来我们试着创建多个线程。 mythread.cc: #include iostream #include vector #include string #include cstdio #include pthread.h #include unistd.husing namespace std;void *start_routine(void *args) {string name static_castconst char *(args); // 安全的进行强制类型转换while (true){cout new thread create success, name: name endl;sleep(1);} }int main() {vectorpthread_t tids; #define NUM 10for (int i 0; i NUM; i){pthread_t tid;char namebuffer[64];snprintf(namebuffer, sizeof(namebuffer), %s:%d, thread, i);pthread_create(tid, nullptr, start_routine, namebuffer);//sleep(1);}while (true){cout new thread create success, name: main thread endl;sleep(1);}return 0; } 注意可能导致 1、名称重复问题 新线程被创建出来谁先运行并不确定是由CPU调度器决定的。传递给pthread_create的是缓冲区的地址namebuffer当作参数传递给线程函数start_routine。线程创建是异步操作可能创建出来的线程还没来得及执行主线程就开始执行了namebuffer就会被覆盖即新线程也许会在namebuffer被下一次循环覆盖后才开始执行。所以多个线程读取到的可能是同一个已经被修改的namebuffer进而造成输出中多个线程输出相同的名称像输出里多次出现的thread4、thread6、thread8等。 2.、输出混乱问题 输出中出现类似 “new thread create successname:  new thread create successname : thread:4 这样混乱的情况是因为多个线程同时对标准输出(std::cout)进行写入操作。std::cout并非线程安全的当多个线程同时向其写入数据时就可能导致输出内容相互穿插、混乱。 我们将代码调整一下 for (int i 0; i NUM; i){pthread_t tid;char namebuffer[64];snprintf(namebuffer, sizeof(namebuffer), %s:%d, thread, i);pthread_create(tid, nullptr, start_routine, namebuffer);sleep(1);} 注意 1、不加sleep 名称重复问题加剧由于线程创建是异步的且多个线程共享同一个namebuffer如果不加sleep线程创建速度会很快namebuffer会被快速覆盖更多的线程可能会读取到相同的被修改后的namebuffer导致输出中名称重复的现象更加严重。 2、加sleep 名称重复问题缓解每次创建线程后休眠 1 秒给新线程足够的时间读取namebuffer中的名称减少了namebuffer被覆盖的可能性从而在一定程度上缓解了名称重复的问题。 我们想创建多个线程线程大部分资源都是共享的所以这种定义局部变量缓冲区的方式其实是不太对的。应该使用下面这种方式 mythread.cc: #include iostream #include vector #include string #include cstdio #include pthread.h #include unistd.husing namespace std;// 当成结构体使用class ThreadData { public:pthread_t tid;char namebuffer[64]; };void *start_routine(void *args) {sleep(1);vectorThreadData * threads;ThreadData *td static_castThreadData *(args); // 安全的进行强制类型转换int cnt 10;while (cnt){cout new thread create success, name: td-namebuffer cnt: cnt-- endl;sleep(1);}delete td;return nullptr; }int main() {#define NUM 10for (int i 0; i NUM; i){ThreadData *td new ThreadData();snprintf(td-namebuffer, sizeof(td-namebuffer), %s:%d, thread, i 1);pthread_create(td-tid, nullptr, start_routine, td);}while (true){cout new thread create success, name: main thread endl;sleep(1);}return 0; } 可以将创建的线程打印出来看一下。 mythread.cc: #include iostream #include vector #include string #include cstdio #include pthread.h #include unistd.husing namespace std;// 当成结构体使用class ThreadData { public:pthread_t tid;char namebuffer[64]; };void *start_routine(void *args) {sleep(1);vectorThreadData * threads;ThreadData *td static_castThreadData *(args); // 安全的进行强制类型转换int cnt 10;while (cnt){cout new thread create success, name: td-namebuffer cnt: cnt-- endl;sleep(1);}delete td;return nullptr; }int main() {vectorThreadData * threads; #define NUM 10for (int i 0; i NUM; i){ThreadData *td new ThreadData();snprintf(td-namebuffer, sizeof(td-namebuffer), %s:%d, thread, i 1);pthread_create(td-tid, nullptr, start_routine, td);threads.push_back(td);}for (auto iter : threads){cout create thread: iter-namebuffer : iter-tid success endl;}while (true){cout new thread create success, name: main thread endl;sleep(1);}return 0; }每一个线程都会new一个ThreadData对象。将该对象传递给pthread_create。作为start_routine的参数。  当线程执行start_toutine函数内部的变量ThreadData *td、cnt会不会影响其他线程呢 mythread.cc: #include iostream #include vector #include string #include cstdio #include pthread.h #include unistd.husing namespace std;// 当成结构体使用class ThreadData { public:pthread_t tid;char namebuffer[64]; };void *start_routine(void *args) {sleep(1);vectorThreadData * threads;ThreadData *td static_castThreadData *(args); // 安全的进行强制类型转换int cnt 10;while (cnt){cout cnt: cnt cnt cnt endl;cnt--;//cout new thread create success, name: td-namebuffer cnt: cnt-- endl;sleep(1);}delete td;return nullptr; }int main() {vectorThreadData * threads; #define NUM 10for (int i 0; i NUM; i){ThreadData *td new ThreadData();snprintf(td-namebuffer, sizeof(td-namebuffer), %s:%d, thread, i 1);pthread_create(td-tid, nullptr, start_routine, td);threads.push_back(td);}for (auto iter : threads){cout create thread: iter-namebuffer : iter-tid success endl;}while (true){cout new thread create success, name: main thread endl;sleep(1);}return 0; } 在语言级别上在函数内部定义的变量属于局部变量具有临时性这里依旧使用。在多线程情况下也没问题每一个线程都有自己独立的栈结构 在多线程环境中每个线程都有自己独立的栈结构。这意味着每个线程调用同一个函数时都会在自己的栈上为函数内部的局部变量分配内存各个线程的局部变量是相互独立的一个线程对其局部变量的操作不会影响其他线程的同名局部变量。 线程终止 return #include iostream #include vector #include string #include cstdio #include pthread.h #include unistd.husing namespace std;// 当成结构体使用class ThreadData { public:pthread_t tid;char namebuffer[64]; };void *start_routine(void *args) {sleep(1);vectorThreadData * threads;ThreadData *td static_castThreadData *(args); // 安全的进行强制类型转换int cnt 10;while (cnt){cout cnt: cnt cnt cnt endl;cnt--;//cout new thread create success, name: td-namebuffer cnt: cnt-- endl;sleep(1);//使用return终止return nullptr;}delete td;//return nullptr; }int main() {vectorThreadData * threads; #define NUM 10for (int i 0; i NUM; i){ThreadData *td new ThreadData();snprintf(td-namebuffer, sizeof(td-namebuffer), %s:%d, thread, i 1);pthread_create(td-tid, nullptr, start_routine, td);threads.push_back(td);}for (auto iter : threads){cout create thread: iter-namebuffer : iter-tid success endl;}while (true){cout new thread create success, name: main thread endl;sleep(1);}return 0; } 可以使用return直接终止线程。 pthread_exit #include iostream #include vector #include string #include cstdio #include pthread.h #include unistd.husing namespace std;// 当成结构体使用class ThreadData { public:pthread_t tid;char namebuffer[64]; };void *start_routine(void *args) {sleep(1);vectorThreadData * threads;ThreadData *td static_castThreadData *(args); // 安全的进行强制类型转换int cnt 10;while (cnt){cout cnt: cnt cnt cnt endl;cnt--;//cout new thread create success, name: td-namebuffer cnt: cnt-- endl;sleep(1);//使用pthread_exit终止pthread_exit(nullptr);}delete td;//return nullptr; }int main() {vectorThreadData * threads; #define NUM 10for (int i 0; i NUM; i){ThreadData *td new ThreadData();snprintf(td-namebuffer, sizeof(td-namebuffer), %s:%d, thread, i 1);pthread_create(td-tid, nullptr, start_routine, td);threads.push_back(td);}for (auto iter : threads){cout create thread: iter-namebuffer : iter-tid success endl;}while (true){cout new thread create success, name: main thread endl;sleep(1);}return 0; } 线程等待 pthread_join 线程也是要被等待的如果不等待会造成类似僵尸进程的问题内存泄漏。 1、获取新线程的退出信息。已经退出的线程其空间没有被释放仍然在进程的地址空间。 2、回收新线程对应的“TCB”等内核资源防止内核泄漏这个现象我们暂时无法查看。 成功返回0失败返回错误码。 mythread.cc: #include iostream #include vector #include string #include cstdio #include cassert #include pthread.h #include unistd.husing namespace std;// 当成结构体使用class ThreadData { public:pthread_t tid;char namebuffer[64]; };void *start_routine(void *args) {// sleep(1);vectorThreadData * threads;ThreadData *td static_castThreadData *(args); // 安全的进行强制类型转换int cnt 10;while (cnt){cout cnt: cnt cnt cnt endl;cnt--;// cout new thread create success, name: td-namebuffer cnt: cnt-- endl;sleep(1);// 使用return终止// return nullptr;}// delete td;pthread_exit(nullptr); }int main() {vectorThreadData * threads; #define NUM 10for (int i 0; i NUM; i){ThreadData *td new ThreadData();snprintf(td-namebuffer, sizeof(td-namebuffer), %s:%d, thread, i 1);pthread_create(td-tid, nullptr, start_routine, td);threads.push_back(td);}for (auto iter : threads){cout create thread: iter-namebuffer : iter-tid success endl;}// 线程等待for (auto iter : threads){int n pthread_join(iter-tid, nullptr);assert(0 n);cout join : iter-namebuffer success endl;delete iter;}cout main thread quit endl; 线程退出时的返回值问题 无论是return退出还是调用pthread_exit返回都要传入一个参数void*类型的。 当线程退出时pthread_join获取线程的退出结果。 参数类型都是void*类型。 如果我们想在线程退出的时候获取我们自定义的返回值线程编号。 mythread.cc: #include iostream #include vector #include string #include cstdio #include cassert #include pthread.h #include unistd.husing namespace std;class ThreadData { public:int number;pthread_t tid;char namebuffer[64]; };void *start_routine(void *args) {ThreadData *td static_castThreadData *(args); int cnt 10;while (cnt){cout cnt: cnt cnt cnt endl;cnt--;sleep(1);}return (void* )td-number; //void* ret (void*)td-number;//这里把int类型的8字节数据转换为4字节的指针会警告但是我们不用管。 }int main() {vectorThreadData * threads; #define NUM 10for (int i 0; i NUM; i){ThreadData *td new ThreadData();td-number i 1;snprintf(td-namebuffer, sizeof(td-namebuffer), %s:%d, thread, i 1);pthread_create(td-tid, nullptr, start_routine, td);threads.push_back(td);}for (auto iter : threads){cout create thread: iter-namebuffer : iter-tid success endl;}// 线程等待for (auto iter : threads){void *ret nullptr;//注意是void*int n pthread_join(iter-tid, ret);//函数内部会做 egvoid** retp ret; *retp return (void*) td-number;assert(0 n);cout join : iter-namebuffer success, number: (long long)ret endl;delete iter;}cout main thread quit endl;return 0; } 既然假的地址整数的地址都能被外部拿到那么返回的是堆空间的地址呢对象的地址呢 #include iostream #include vector #include string #include cstdio #include cassert #include pthread.h #include unistd.husing namespace std;class ThreadData { public:int number;pthread_t tid;char namebuffer[64]; };class ThreadReturn { public:int exit_code;int exit_result; };void *start_routine(void *args) {ThreadData *td static_castThreadData *(args); int cnt 10;while (cnt){cout cnt: cnt cnt cnt endl;cnt--;sleep(1);}// return (void *)td-number; // void* ret (void*)td-number;// 这里把int类型的8字节数据转换为4字节的指针会警告但是我们不用管。// 既然假的地址整数的地址都能被外部拿到那么返回的是堆空间的地址呢对象的地址呢ThreadReturn *tr new ThreadReturn();// 假设tr-exit_code 1;tr-exit_result 222;return (void *)tr;// 注意下面这种写法是错误的。// ThreadReturn tr;// tr-exit_code1;// tr-exit_result222;// return (void*)tr;// tr是在栈上开辟的空间出了栈就被销毁了。 }int main() {vectorThreadData * threads; #define NUM 10for (int i 0; i NUM; i){ThreadData *td new ThreadData();td-number i 1;snprintf(td-namebuffer, sizeof(td-namebuffer), %s:%d, thread, i 1);pthread_create(td-tid, nullptr, start_routine, td);threads.push_back(td);}for (auto iter : threads){cout create thread: iter-namebuffer : iter-tid success endl;}// 线程等待for (auto iter : threads){ThreadReturn *ret nullptr;// void *ret nullptr; // 注意是void*int n pthread_join(iter-tid, (void **)ret); // 函数内部会做 egvoid** retp ret; *retp return (void*) td-number;assert(0 n);// cout join : iter-namebuffer success, number: (long long)ret endl;cout join : iter-namebuffer success, exit_code: ret-exit_code ,exit_result: ret-exit_result endl;delete iter;}cout main thread quit endl;// while (true)// {// cout new thread create success, name: main thread endl;// sleep(1);// }return 0; } 调用该函数的线程将挂起等待直到ID为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的  如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。  如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_ CANCELED。  如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。  如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。 这里有一个问题线程退出的时候为什么没有见到线程退出对应的退出信号呢 线程出异常收到信号整个进程都会退出pthread_join默认认为函数会调用成功不考虑异常问题异常问题是进程考虑的问题 线程取消 pthread_cacel 线程是可以被取消的注意线程要被取消的前提是这个线程已经跑起来了正在执行。 #include iostream #include vector #include string #include cstdio #include cassert #include pthread.h #include unistd.husing namespace std;class ThreadData { public:int number;pthread_t tid;char namebuffer[64]; };class ThreadReturn { public:int exit_code;int exit_result; };void *start_routine(void *args) {// sleep(1);ThreadData *td static_castThreadData *(args); int cnt 10;while (cnt){cout cnt: cnt cnt cnt endl;cnt--;sleep(1);}return (void *)111; }int main() {vectorThreadData * threads; #define NUM 10//线程创建for (int i 0; i NUM; i){ThreadData *td new ThreadData();td-number i 1;snprintf(td-namebuffer, sizeof(td-namebuffer), %s:%d, thread, i 1);pthread_create(td-tid, nullptr, start_routine, td);threads.push_back(td);}for (auto iter : threads){cout create thread: iter-namebuffer : iter-tid success endl;}// 线程取消sleep(5);for (int i 0; i threads.size() / 2; i){pthread_cancel(threads[i]-tid);cout pthread_cancel : threads[i]-namebuffer success endl;}// 线程等待for (auto iter : threads){void *ret nullptr;int n pthread_join(iter-tid, ret);assert(0 n);cout join : iter-namebuffer success, exit_code: (long long)ret endl;delete iter;}cout main thread quit endl;return 0; } 线程如果是被取消的退出码为-1。 分离线程 线程是可以等待的等待的时候join的等待是阻塞式等待。如果我们不想等待呢 默认情况下新创建的线程是joinable的线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成系统泄漏。 如果不关心线程的返回值join是一种负担这个时候我们可以告诉系统当线程退出时自动释放线程资源。 pthread_self 获取线程ID。 mythread.cc: #include iostream #include unistd.h #include cstring #include string #include pthread.h// 获取线程id std::string changeId(const pthread_t thread_id) {char tid[128];snprintf(tid, sizeof(tid), 0x%x, thread_id);return tid; }void *start_routine(void *args) {std::string threadname static_castconst char *(args);int cnt 5;while (cnt--){std::cout threadname running... changeId(pthread_self()) std::endl;sleep(1);}return nullptr; }int main() {pthread_t tid;pthread_create(tid, nullptr, start_routine, (void *)thread 1);std::string main_id changeId(pthread_self());std::cout main thread running ... new thread id: changeId(tid) main thread id: main_id std::endl;int n pthread_join(tid, nullptr);std::cout result: n : strerror(n) std::endl;return 0; } pthread_detach 分离线程。 mythread.cc: #include iostream #include unistd.h #include cstring #include string #include pthread.h// 获取线程id std::string changeId(const pthread_t thread_id) {char tid[128];snprintf(tid, sizeof(tid), 0x%x, thread_id);return tid; }void *start_routine(void *args) {std::string threadname static_castconst char *(args);pthread_detach(pthread_self());//设置自己为分离状态int cnt 5;while (cnt--){std::cout threadname running... changeId(pthread_self()) std::endl;sleep(1);}return nullptr; }int main() {pthread_t tid;pthread_create(tid, nullptr, start_routine, (void *)thread 1);std::string main_id changeId(pthread_self());std::cout main thread running ... new thread id: changeId(tid) main thread id: main_id std::endl;//一个线程默认是joinable的如果设置了分离状态不能进行等待了int n pthread_join(tid, nullptr);std::cout result: n : strerror(n) std::endl;return 0; } 新线程不是分离了吗怎么还是被join了。 新线程和主线程创建出来谁先运行并不确定。那么就可能存在新线程还没有pthread_detach分离主线程就已经pthread_join阻塞等待了新线程再分离主线程也不知道。 我们来证明一下上面的说法让主线程慢一下sleep两秒新线程此时一定已经分离了。 mythread.cc: #include iostream #include unistd.h #include cstring #include string #include pthread.h// 获取线程id std::string changeId(const pthread_t thread_id) {char tid[128];snprintf(tid, sizeof(tid), 0x%x, thread_id);return tid; }void *start_routine(void *args) {std::string threadname static_castconst char *(args);pthread_detach(pthread_self());//设置自己为分离状态int cnt 5;while (cnt--){std::cout threadname running... changeId(pthread_self()) std::endl;sleep(1);}return nullptr; }int main() {pthread_t tid;pthread_create(tid, nullptr, start_routine, (void *)thread 1);std::string main_id changeId(pthread_self());std::cout main thread running ... new thread id: changeId(tid) main thread id: main_id std::endl;sleep(2);//一个线程默认是joinable的如果设置了分离状态不能进行等待了int n pthread_join(tid, nullptr);std::cout result: n : strerror(n) std::endl;return 0; } 这样做是不是有点不太好啊新线程要分离主线程还要sleep慢一点。其实在分离线程可以让主线程分离新线程。 mythread.cc: #include iostream #include unistd.h #include cstring #include string #include pthread.h// 获取线程id std::string changeId(const pthread_t thread_id) {char tid[128];snprintf(tid, sizeof(tid), 0x%x, thread_id);return tid; }void *start_routine(void *args) {std::string threadname static_castconst char *(args);//pthread_detach(pthread_self());//设置自己为分离状态int cnt 5;while (cnt--){std::cout threadname running... changeId(pthread_self()) std::endl;sleep(1);}return nullptr; }int main() {pthread_t tid;pthread_create(tid, nullptr, start_routine, (void *)thread 1);std::string main_id changeId(pthread_self());pthread_detach(tid);std::cout main thread running ... new thread id: changeId(tid) main thread id: main_id std::endl;//sleep(2);//一个线程默认是joinable的如果设置了分离状态不能进行等待了int n pthread_join(tid, nullptr);std::cout result: n : strerror(n) std::endl;return 0; } 一旦设置为分离状态主线程就不需要等待了可以做自己的事情不用再关心新线程了。joinable和分离是冲突的一个线程不能既是joinable又是分离的。 mythread.cc: #include iostream #include unistd.h #include cstring #include string #include pthread.h// 获取线程id std::string changeId(const pthread_t thread_id) {char tid[128];snprintf(tid, sizeof(tid), 0x%x, thread_id);return tid; }void *start_routine(void *args) {std::string threadname static_castconst char *(args);// pthread_detach(pthread_self());//设置自己为分离状态int cnt 5;while (cnt--){std::cout threadname running... changeId(pthread_self()) std::endl;sleep(1);}return nullptr; }int main() {pthread_t tid;pthread_create(tid, nullptr, start_routine, (void *)thread 1);std::string main_id changeId(pthread_self());pthread_detach(tid);std::cout main thread running ... new thread id: changeId(tid) main thread id: main_id std::endl;// sleep(2);// 一个线程默认是joinable的如果设置了分离状态不能进行等待了// int n pthread_join(tid, nullptr);// std::cout result: n : strerror(n) std::endl;while (true){std::cout main thread running ... new thread id: changeId(tid) main thread id: main_id std::endl;sleep(1);}return 0; } 初步重新认识线程库语言版 C也支持多线程。 makefile: mythread.cc: #include iostream #include unistd.h #include threadvoid thread_run() {while (true){std::cout 我是新线程... std::endl;sleep(1);} }int main() {std::thread t1(thread_run);while (true){std::cout 我是主线程... std::endl;sleep(1);}t1.join();return 0; } makefile:  任何语言在Linux中如果要实现多线程必定要使用pthread库。 如何看待C11中的多线程呢 C11的多线程在Linux环境中本质是对pthread库的封装。 语言能对pthread库做封装这份代码在WINDOWS下也能运行在WINDOWS中语言帮我们解决了平台的差异化问题是跨平台的。使用原生线程库创建线程叫做不可跨平台只能在Linux下跑。 线程ID pthread_t类型的线程ID本质就是一个进程地址空间上的一个地址。 每个线程都有自己的独立栈结构。但是主线程的进程地址空间栈只有一个。 如何保证每个线程都有自己独立的栈结构呢 原生线程库中可能要存在多个线程你用这个接口创建线程别人可能同时在使用。原生线程库也要对线程做管理先描述线程属性线程id独立栈比较少再组织。 可以理解为线程是库实现了一部分OS实现了一部分。当我们每创建一个线程库都要创建线程所对应的结构体来进行线程控制。每一个结构体对应一个轻量级进程。在Linux中这种线程称为用户级线程。 库不关心线程怎么调度只关心线程是谁线程id栈大小等其他属性。 用户级线程id究竟是什么 线程被创建出来之后根据线程id就能找到这个线程和线程对应的属性。用地址标识线程当用户想使用某个线程时拿着线程id就可以对线程进行相关操作了。 线程执行完库自动将线程返回结果填到该线程id指向的共享区的某个空间。join的时候根据线程id就能拿到发过来的返回值。 所以主线程的栈是在进程地址空间其他线程的栈在mmap区域中。 创建轻量级进程是库创建的创建完之后将线程id起始地址传给void *child_stack。底层在使用时使用的就是当前线程的栈而不是主线程的栈。 总结 主线程是进程启动时默认创建的线程它的栈空间通常由操作系统在创建进程时分配。这个栈空间用于存储主线程执行过程中的局部变量、函数调用信息如返回地址、参数等。主线程栈的大小一般由操作系统或编译环境决定并且在进程的地址空间中有明确的位置。当使用线程库创建线程时库会负责为新线程分配栈空间。这个栈空间是在进程的地址空间内进行分配的并且是新线程独有的用来存储该线程的局部变量、函数调用上下文等信息。在底层调用clone系统调用创建线程时需要把新线程栈的起始地址传递给clone函数。在pthread库的实现中会将分配好的栈的起始地址传递给相关的底层函数。 线程库的作用 线程库在创建线程时会向操作系统请求分配一块内存区域作为新线程的栈。线程库负责管理线程栈的分配和释放确保每个线程都有足够的栈空间来执行其代码。 线程的局部存储 mythread.cc: #include iostream #include unistd.h #include cstring #include string #include pthread.hint g_val 100;// 获取线程id std::string changeId(const pthread_t thread_id) {char tid[128];snprintf(tid, sizeof(tid), 0x%x, thread_id);return tid; }void *start_routine(void *args) {std::string threadname static_castconst char *(args);// pthread_detach(pthread_self());//设置自己为分离状态int cnt 5;while (true){std::cout threadname running... changeId(pthread_self()) g_val: g_val g_val g_val std::endl;g_val;sleep(1);}return nullptr; }int main() {pthread_t tid;pthread_create(tid, nullptr, start_routine, (void *)thread 1);std::string main_id changeId(pthread_self());pthread_detach(tid);std::cout main thread running ... new thread id: changeId(tid) main thread id: main_id std::endl;// sleep(2);// 一个线程默认是joinable的如果设置了分离状态不能进行等待了// int n pthread_join(tid, nullptr);// std::cout result: n : strerror(n) std::endl;while (true){std::cout main thread running ... new thread id: changeId(tid) main thread id: main_id g_val: g_val g_val g_val std::endl;sleep(1);}return 0; } 全局变量g_val被两个线程共享。 添加__thread可以将一个内置类型设置为局部存储。 依旧是全局变量只不过在编译的时候给每个线程都有一份这样线程在访问这个变量时就不会互相影响。它为每个线程分配独立的变量副本每个线程对该变量的操作不会影响其他线程中的同名变量。 第一次地址0x6020d4第二次地址0x74f9d7176fc。地址差别这么大? 第一次全局变量在已初始化区域为低地址第二次将它设置为局部存储是在mmap区域为高地址。 以上是线程控制的全部内容关于线程更多的细节问题请看下篇...
http://www.laogonggong.com/news/128722.html

相关文章:

  • 怎么做国际货运代理外贸网站wordpress 初始化
  • 网站建设的产品类型是什么苏州响应式网站建设
  • 黑龙江专业建站磁力搜索器在线
  • 淘客客怎么做自己的网站应用公园是收费还是免费的
  • 深圳十大装饰公司名单合肥百度快照优化排名
  • 搜狐网站网络营销怎么做学校网站模板 中文
  • ps做的网站模板简述阐述网站建设的步骤过程
  • 电子商务网站的优点有那些做网上推广
  • 宁波网站建设公司费用价格php网站如何导入数据库
  • 网站建设举报建立网络的流程
  • 云网站注册荣耀官方网站手机商城
  • 团购网站 模板网站中转页怎么做
  • 网站线下推广方式阿里企业邮箱app下载
  • 专业做网站哪家好嘉兴网站建设外包公司
  • 花生壳怎么做网站富阳做网站的
  • 炫酷网站源码下载未明潮网站建设保密协议
  • 做五金的网站做境外域名网站
  • 创建网站要申请域名吗免费淘宝客网站模板下载
  • 通信网站模板男科医院治疗一次2000元
  • 福州精美个人网站建设公司中国商务服务网
  • 牡丹江网站建设公司国内最大的搜索引擎
  • 成都销售型网站怎么通过做网站赚钱
  • 比较好的平面设计网站公司网站改版多少钱
  • 8个页面的网站怎么做广州牌手表网站
  • 景区网站的作用制作网站软件作品
  • 北京网站建设方案建设公司wordpress去掉页脚
  • 云朵课堂网站开发怎么收费应届生招聘去哪个网站
  • 一个网站做app免费网页制作工具下载
  • 怎样制作网站和软件百度快速优化推广
  • 网站虚拟域名微信哪家公司开发的