Linux-线程终止-线程等待-线程分离-线程安全
发布日期:2021-05-08 20:30:30 浏览次数:22 分类:精选文章

本文共 7196 字,大约阅读时间需要 23 分钟。

文章目录

1. 线程终止

1.1 pthread_exit函数

在这里插入图片描述

  void pthread_exit(void *retval);

作用: 谁调用谁退出

参数:
  retval:线程A结束的时候传递给等待线程B的参数
验证:

#include 
#include
#include
#define a 4 struct ThreadId{ int thread_id_;}; void* MyThreadStart(void* arg) { struct ThreadId* ti = (struct ThreadId*)arg; printf("i am MyThreadStart, i = %d\n", ti->thread_id_); sleep(1); pthread_exit(NULL); printf("pthread_exit, i = %d\n", ti->thread_id_); delete ti; }int main(){ pthread_t tid; for(int i = 0; i < a; i++) { struct ThreadId* ti = new ThreadId(); ti->thread_id_ = i; int ret = pthread_create(&tid,NULL,MyThreadStart,(void*)ti); if(ret < 0) { perror("pthread_create"); return 0; } } //创建成功 while(1) { printf("i am main thread\n"); sleep(1); } return 0; }

在这里插入图片描述

  如果线程退出,打印如图所示,如果线程未退出,每一个线程会打印两句话。

1.2 线程的入口函数代码执行完毕,线程退出

1.3 pthread_cancel函数

在这里插入图片描述

  int pthread_cancel(pthread_t thread);

功能: 根据线程的标识符终止对应线程

参数:
  thread:被终止的线程的标识符
获取自己的线程标识符: pthread_t pthread_self(void);
在这里插入图片描述
验证:

#include 
#include
#include
#define THREAD_NUM 4 struct ThreadId { int thread_id_; }; void* MyThreadStart(void* arg) { struct ThreadId* ti = (struct ThreadId*)arg; //while(1) { printf("i am MyThreadStart, i = %d\n", ti->thread_id_); sleep(1); } pthread_cancel(pthread_self()); delete ti; } int main(){ pthread_t tid[THREAD_NUM]; for(int i = 0; i < THREAD_NUM; i++) { struct ThreadId* ti = new ThreadId(); ti->thread_id_ = i; int ret = pthread_create(&tid[i], NULL, MyThreadStart, (void*) ti); if(ret < 0) { perror("pthread_create"); return 0; } } //pthread_cancel(pthread_self()); while(1) { printf("i am main thread\n"); sleep(1); } return 0;}

在这里插入图片描述

在这里插入图片描述
  如果主线程自己取消自己,调用pthread_cancel(pthread_self),那么主线程的状态变为僵尸状态(Z),工作线程正常,整个进程没有退出。

验证:

#include 
#include
#include
#define THREAD_NUM 4 struct ThreadId{ int thread_id_;};void* MyThreadStart(void* arg){ struct ThreadId* ti = (struct ThreadId*)arg; while(1) { printf("i am MyThreadStart, i = %d\n", ti->thread_id_); sleep(1); } delete ti;}int main(){ pthread_t tid[THREAD_NUM]; for(int i = 0; i < THREAD_NUM; i++) { struct ThreadId* ti = new ThreadId(); ti->thread_id_ = i; int ret = pthread_create(&tid[i], NULL, MyThreadStart, (void*)ti); if(ret < 0) { perror("pthread_create"); return 0; } } pthread_cancel(pthread_self()); while(1) { printf("i am main thread\n"); sleep(1); } return 0;}

在这里插入图片描述

在这里插入图片描述

2. 线程等待

2.1 原因

  由于线程的默认属性为joinable属性,当线程退出的时候其资源不会被操作系统回收,需要其他线程来进行进程等待,继续回收,否则就会造成内存泄露。

2.2 接口

  int pthread_join(pthread_t thread, void **retval);

在这里插入图片描述
  thread:需要等待的线程的标识符
  retval:线程退出时的返回值

  1.线程入口函数退出的时候,retval就是线程入口函数的返回值

  2.pthread_exit(void* retval),retval就是pthread_exit函数的参数值
  3.pthread_cancel:retval的值是一个常数PTHREAD_CANCELED,调用pthread_join函数进行等待的执行流如果还没有等待到退出的线程,则当前调用pthread_join函数的执行流就会阻塞。

2.3 验证

#include 
#include
#include
#define THREAD_NUM 4 struct ThreadId { int thread_id_; }; void* MyThreadStart(void* arg) { struct ThreadId* ti = (struct ThreadId*)arg; while(1) { printf("i am MyThreadStart, i = %d\n", ti->thread_id_); sleep(1); } delete ti; return NULL; } int main(){ pthread_t tid[THREAD_NUM]; for(int i = 0; i < THREAD_NUM; i++) { struct ThreadId* ti = new ThreadId(); ti->thread_id_ = i; int ret = pthread_create(&tid[i], NULL, MyThreadStart, (void*)ti); if(ret < 0) { perror("pthread_create"); return 0; } } for(int i = 0; i < THREAD_NUM; i++) { pthread_join(tid[i], NULL); } while(1) { printf("i am main thread\n"); sleep(1); } return 0;}

在这里插入图片描述

在这里插入图片描述

3. 线程分离

3.1 概念

  一个进程的属性如果从joinable属性变成detach属性,则当前这个线程在退出的时候,不需要其他线程回收资源,操作系统会自己回收资源。

3.2 接口

在这里插入图片描述

  int pthread_detach(pthread_t thread);

参数: pthread_t thread

  如果工作线程自己分离自己,要写在工作线程代码的最前面。

3.3 验证

#include 
#include
#include
#define THREAD_NUM 4struct ThreadId{ int thread_id_;}; void* MyThreadStart(void* arg){ pthread_detach(pthread_self()); struct ThreadId* ti = (struct ThreadId*)arg; //while(1) { printf("i am MyThreadStart, i = %d\n", ti->thread_id_); sleep(1); } delete ti; return NULL;}int main(){ pthread_t tid[THREAD_NUM]; for(int i = 0; i < THREAD_NUM; i++) { struct ThreadId* ti = new ThreadId(); ti->thread_id_ = i; int ret = pthread_create(&tid[i], NULL, MyThreadStart, (void*)ti); if(ret < 0) { perror("pthread_create"); return 0; } } while(1) { printf("i am main thread\n"); sleep(1); } return 0; }

在这里插入图片描述在这里插入图片描述

4. 线程安全

4.1 概念

  多个执行流,访问临界资源,不会导致程序产生二义性。

    执行流: 理解为线程
    访问: 指的是对临界资源进行操作
    临界资源: 指的是多个线程都可以访问到的资源
      例如:全局变量,某个结构体变量,某个类的实例化指针
    临界区: 代码操作临界资源的代码区域称之为临界区
    二义性: 结果会有多个

4.2 原理

在这里插入图片描述

  正常来说,第一步,内存当中有一个i = 10,寄存器从内存中读到i = 10,第二步,CPU从寄存器中拿到i的值进行计算,第三步,CPU将计算的结果写回到寄存器,第四步,寄存器将写回的值在内存中进行修改。

4.3 线程不安全的现象

  内存中我们有一个i = 10,我们假设有两个线程,两个线程都是对于i进行加1操作。线程1先开始执行,当线程1把i读到寄存器后,由于计算机是分时轮转,这时候时间片用完了,线程1被切换出去了,此时线程1在寄存其中i的值是10,之后线程2被切换进来了,线程2从内存当中读到i的值为10,经过CPU进行运算,写回到寄存器,寄存器再存到内存当中,这时候i在内存当中的值被修改为了11,线程2结束后,线程1又被切换回来了,线程1通过保存的上下文信息知道i此时的值是10,通过程序计数器知道要在CPU当中进行加1操作,经过CPU进行运算,写回到寄存器,寄存器再存到内存当中,这时候i在内存当中的值仍然是11,但是实际上我们对i执行了两次加1操作,正确的值是12,这时候代码的结果就存在了二义性。

4.4 验证

#include 
#include
#include
#define THREAD_NUM 2int g_tickets = 10;void* MyThreadStart(void* arg){ while(1) { if(g_tickets > 0) { printf("i have %d, i am %p\n", g_tickets, pthread_self()); g_tickets--; } else { pthread_exit(NULL); } } return NULL;}int main(){ pthread_t tid[THREAD_NUM]; for(int i = 0; i < THREAD_NUM; i++) { int ret = pthread_create(&tid[i], NULL, MyThreadStart, NULL); if(ret < 0) { perror("pthread_create"); return 0; } } for(int i = 0; i < THREAD_NUM; i++) { pthread_join(tid[i], NULL); } printf("pthread_join end...\n"); return 0;}
上一篇:Python tkinter 多线程 哲学家进餐问题
下一篇:leetcode-二维数组中的查找-49

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2025年03月21日 10时58分45秒