news 2026/7/10 22:46:16

多任务并发之线程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
多任务并发之线程

线程

什么是线程

  • 线程是一个轻量级的进程,用来实现多任务并发。
  • 线程是操作系统任务调度的最小单位。
  • 线程由某个进程创建

线程的创建

  • 线程由所在进程创建,创建时,进程为其分配独立的栈区空间(默认8M)。
  • 同一个进程中的线程,共用所在进程的堆区、数据区、文本区

线程的调度

宏观并行:从宏观角度,多个任务同时执行

微观串行:微观角度,多个任务按照一定顺序先后执行

线程的消亡

线程退出
回收线程的资源空间

进程和线程的区别

线程相关接口

pthread_create函数

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 功能:创建并启动一个线程 参数: thread :保存线程ID的变量地址 attr :设置线程属性 NULL :按照默认属性创建 start_routine :函数指针,线程启动后要执行的任务的函数地址(线程任务函数) arg :传递给线程任务函数的参数 返回值: 成功:0 失败:非0 注意:1. 编译和链接时要加-lpthread, 表示链接线程库。 2. 线程执行时,线程所在的进程不能先退出

pthread_exit()函数

void pthread_exit(void *retval); 功能:结束一个线程 参数: retval :可以用来保存线程退出时传递的参数 和return返回的指针类似 注意: 线程退出: 1. 在线程任务中return 2. 在线程任务中pthread_exit 3. 在线程中exit,让所在进程直接退出

phtread_self()函数

功能:获取当前线程的tid号

pthread_join()函数

int pthread_join(pthread_t thread, void **retval); 功能:等待回收具有非分离属性的线程 参数: thread:要回收的线程ID; retval:接收回收的线程退出时传递的参数:通过pthread_exit参数,return返回值传递 NULL : 不接收参数 返回值: 成功:0 失败:非0

示例:

#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void *th_task1(void *arg) { int cnt = 0; int num = *((int *)arg); while (1) { if (5 == cnt++) { // exit(0);//直接退出整个进程 pthread_exit(NULL); // return NULL; } printf("thread1 tid is %lx,num is %d\n", pthread_self(), num); sleep(1); } return NULL; } void *th_task2(void *arg) { while (1) { printf("thread2 tid is %lx\n", pthread_self()); sleep(1); } } int main(int argc, char **argv) { pthread_t tid[2]; int num = 100; int ret = pthread_create(&tid[0], NULL, th_task1, &num); if (ret != 0) { fprintf(stderr, "pthread_create error\n"); return -1; } ret = pthread_create(&tid[1], NULL, th_task2, NULL); if (ret != 0) { fprintf(stderr, "pthread_create error\n"); return -1; } pthread_join(tid[0], NULL); pthread_join(tid[1], NULL); return 0; }

线程的分离属性

分离属性 :不需要被其他线程回收的线程,称为具有分离属性的线程,结束时可以被操作系统回收。
非分离属性:可以被其他线程回收或者结束的线程称为非分离属性的线程
默认属性:非分离属性
设置线程分离属性:

int pthread_detach(pthread_t thread); 功能:设置线程的分离属性 参数: thread:要设置的线程的线程ID 返回值: 成功:0 失败:!0
include <stdio.h> #include <pthread.h> void *task(void *arg) { } int main(int argc, const char *argv[]) { pthread_t tid; int cnt = 0; while (1) { int ret = pthread_create(&tid, NULL, task, NULL); if (ret != 0) { fprintf(stderr, "pthread_create error\n"); return -1; } pthread_detach(tid); cnt++; printf("cnt = %d\n", cnt); } return 0; }

线程的互斥机制

多线程访问临界资源时,存在资源竞争问题。
临界资源:多个线程可以同时操作的资源,比如:全局变量,共享内存等。
如何解决资源竞争问题:
线程的互斥机制:让多个线程在访问临界资源时,具有排他性访问的特性

互斥锁:实现互斥机制

  • 创建互斥锁:pthread_mutex_t
  • 初始化锁:pthread_mutex_init
  • 加锁:pthread_mutex_lock/pthread_mutex_trylock
  • 解锁:pthread_mutex_unlock
  • 销毁锁:pthread_mutex_destroy
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 功能:初始化互斥锁 参数: mutex :互斥锁对象地址 attr: 锁的属性 返回值: 成功:0 失败:非0
int pthread_mutex_lock(pthread_mutex_t *mutex); 功能:以阻塞方式等待加锁 int pthread_mutex_trylock(pthread_mutex_t *mutex); 功能:以非阻塞方式加锁,如果该锁已被使用,则返回 int pthread_mutex_unlock(pthread_mutex_t *mutex); 功能:解锁/释放锁 int pthread_mutex_destroy(pthread_mutex_t *mutex); 功能:销毁锁
#include <stdio.h> #include <pthread.h> int num_g=0; pthread_mutex_t mutex; void *task1(void *arg) { for(int i=0;i<5;i++) { pthread_mutex_lock(&mutex); num_g=num_g+1; printf("num_g=%d\n",num_g); pthread_mutex_unlock(&mutex); } return NULL; } void *task2(void *arg) { for(int i=0;i<5;i++) { pthread_mutex_lock(&mutex); num_g=num_g+1; printf("num_g=%d\n",num_g); pthread_mutex_unlock(&mutex); } return NULL; } int main(int argc, char **argv) { pthread_t tid[2]; pthread_mutex_init(&mutex,NULL); pthread_create(&tid[0],NULL,task1,NULL); pthread_create(&tid[1],NULL,task2,NULL); pthread_join(tid[0],NULL); pthread_join(tid[1],NULL); pthread_mutex_destroy(&mutex); return 0; }

线程间同步机制

线程间同步:让多个线程任务执行某部分任务的时候具有先后顺序。
同步方式:信号量,可以将信号量看成一个资源数,当该资源数>0的时候,可以申请到信号量,当资源时为0时,申请信号量阻塞等待。申请完信号量时,该信号量对应的资源数自动-1。释放对应的信号量时,该信号量对应的资源数自动+1。

创建信号量

sem_t sem;

初始化信号量

int sem_init(sem_t *sem, int pshared, unsigned int value); 功能:初始化信号量 参数: sem:初始化的信号量对象地址 pshared:进程间共享还是线程间 0: 线程间 非0:进程间 value:初始化的资源数 返回值: 成功:0 失败:-1

申请信号量(P操作)

int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem);

释放信号量(V操作)

int sem_post(sem_t *sem);

销毁信号量

int sem_destroy(sem_t *sem);

示例

#include <pthread.h> #include <semaphore.h> #include <stdio.h> char buff[1024] = {0}; sem_t sem_w; sem_t sem_r; void *task1(void *arg) { while (1) { sem_wait(&sem_w); //-1 fgets(buff, sizeof(buff), stdin); sem_post(&sem_r); //+1 } } void *task2(void *arg) { while (1) { sem_wait(&sem_r); //-1 printf("buff = %s\n", buff); sem_post(&sem_w); //+1 } } int main(int argc, const char *argv[]) { pthread_t tid[2]; sem_init(&sem_w, 0, 1); sem_init(&sem_r, 0, 0); pthread_create(&tid[0], NULL, task1, NULL); pthread_create(&tid[1], NULL, task2, NULL); pthread_join(tid[0], NULL); pthread_join(tid[1], NULL); sem_destroy(&sem_w); sem_destroy(&sem_r); return 0; }

死锁

死锁:死锁指的是在多线程环境中,每个执行流(线程)都有未释放的资源,且互相请求对方未释放资 源,从而导致陷入永久等待状态的情况。 现象: 现象1:忘记释放锁 现象2:重复加锁 现象3:多线程多锁,抢占锁资源不当 如:线程A获取了1锁,线程B获取了2锁,同时线程A还想获取2锁,线程B还想获取1锁 *产生死锁的四个必要条件(四条全中 = 死锁,破掉任意一条 = 解除): (1) 互斥条件:一个资源同一时刻只能被一个任务占用(一个执行流获取锁后,其它执行流不能再获取 该锁)。 (2) 请求与保持条件:已经拿着一部分资源不释放,同时又去申请新的资源(执行流本身使用着一把锁 并不释放,还在请求别的锁)。 (3) 不剥夺条件:资源只能由持有者主动释放,系统/其他任务不能强行抢过来(A执行流拿着锁,其它 执行流不能释放)。 (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系(多个执行流拿着对方想要的 锁,并且各执行流还去请求对方的锁)。 解决方法: 1.锁一定要成对出现 2.使线程的加解锁顺序一致 3.破坏环路等待条件 使用非阻塞锁,一旦线程发现请求的锁被使用,就去释放自己拥有的资源
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/10 22:44:20

C语言如何实现C++的默认参数方法

前言 在C里&#xff0c;在声明一个函数时可以显示给参数指定默认参数值&#xff1a; void add(int a, int b 2)这样你直接调用add(1)编译器在构建的时候会默认给第二个参数传递2&#xff0c;但是C语言没有这样的能力&#xff0c;C语言是一个面向底层的语言&#xff0c;它不具备…

作者头像 李华
网站建设 2026/7/10 22:39:11

彻底搞懂 K8s 服务访问:ClusterIP、NodePort 流量机制

前言在 Kubernetes 集群中&#xff0c;Pod 是最小的部署单元&#xff0c;但 Pod 的生命周期是短暂的——当 Pod 发生故障或被重新调度时&#xff0c;其 IP 地址也会随之改变。这就带来一个问题&#xff1a;如果一组 Pod 需要对外提供稳定服务&#xff08;例如 HTTP 服务&#x…

作者头像 李华
网站建设 2026/7/10 22:35:48

Python深入浅出:从入门到工程实践13

第 13 章:并发基础:线程与进程 学习目标: 彻底理解 GIL 对 Python 并发的影响 掌握 threading 和 multiprocessing 的正确用法 能根据场景选择并发模型 13.0 为什么需要并发?——从现实问题出发 想象你在运营一个电商网站,需要完成以下任务: 任务 单线程耗时 说明 查询…

作者头像 李华
网站建设 2026/7/10 22:35:01

sc结构模块图中模块之间的关系(耦合、内聚)

耦合和内聚就是描述sc结构模块图中模块之间的关系 完全正确 内聚&#xff1a;描述单个模块内部元素联系紧密程度耦合&#xff1a;描述多个模块之间相互依赖关联程度 二者都是用来评判SC模块结构图设计好坏的核心指标 一、内聚&#xff08;管自己&#xff09; 定义 一个模块内部…

作者头像 李华
网站建设 2026/7/10 22:34:57

顶背离(Bearish Divergence)

顶背离&#xff08;Bearish Divergence&#xff09; 是技术分析里经典的见顶 / 反转预警信号&#xff1a;价格创新高&#xff0c;但动能指标&#xff08;MACD/RSI/KDJ&#xff09;不创新高、反而走低&#xff0c;说明上涨动能衰竭、多头乏力&#xff0c;大概率要回落。一、核心…

作者头像 李华