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

线程并发编程,同步与互斥机制

线程

概念

线程是一个轻量级的进程,为了提高系统的性能引入线程。

线程和进程都参与统一的调度。

在同一个进程中可以创建多个线程,并且共享进程资源。

进程和线程区别(面试题)

相同点:都为操作系统提供了并发执行的能力

不同点:

资源和调度:进程是系统资源分配的最小单位,线程是资源调度的最小单位

地址空间方面:每个进程都有独立的地址空间;同一个进程中的多个线程共享进程地址空间

通信方面:线程通信相对简单,只需要通过全局变量就可以实现,但是需要考虑临界资源访问的问题; 进程通信比较复杂,需要借助进程间的通信机制(3-4g的内核空间)。

安全性方面:线程安全性差一些,当进程结束时会导致所有线程退出; 进程相对安全。

线程资源

共享的资源:可执行的指令、静态数据、进程中打开的文件描述符、信号处理函数、当前工作目录、用户ID、用户组ID

私有的资源:线程ID (TID)、PC(程序计数器)和相关寄存器、堆栈(局部变量, 返回地址)、错误号 (errno)、信号掩码和优先级、执行状态和属性

函数接口

创建线程:pthread_create

#include<pthread.h>intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);功能:创建线程参数:thread:线程标识attr:线程属性,NULL代表设置默认属性start_routine:函数名:代表线程函数(自己写)arg:用来给前面函数传参返回值:成功:0失败:错误码编译的时候需要加-lpthread 链接动态库

退出线程:pthread_exit

#include<pthread.h>voidpthread_exit(void*retval);功能:用于退出线程的执行参数:retval:线程退出时返回的值
#include <stdio.h>#include <pthread.h>#include <unistd.h>void *handler(void *arg){printf("in the thread\n");pthread_exit(NULL); // 让线程退出while(1);return NULL;}int main(int argc, char const *argv[]){pthread_t tid;if(pthread_create(&tid, NULL, handler, NULL) != 0){perror("create thread err");}// 主线程printf("in the main\n");while(1);return 0;}

回收线程资源

#include <pthread.h>int pthread_join(pthread_t thread, void **retval);功能:用于等待一个指定的线程结束,阻塞函数参数:thread:创建的线程对象,线程IDretval指针*value_ptr指向线程返回的参数,一般为NULL返回值:成功:0失败:errnoint pthread_detach(pthread_t thread);功能:让线程结束时自动回收线程资源,让线程和主线程分离,非阻塞函数参数:thread:线程ID非阻塞式的,例如主线程分离(detach)了线程T2,那么主线程不会阻塞在pthread_detach(),pthread_detach()会直接返回,线程T2终止后会被操作系统自动回收资源
#include <stdio.h>#include <pthread.h>#include <unistd.h>void *handler(void *arg){printf("in the thread\n");sleep(2);pthread_exit(NULL); // 让线程退出while(1);return NULL;}int main(int argc, char const *argv[]){pthread_t tid;if(pthread_create(&tid, NULL, handler, NULL) != 0){perror("create thread err");}// pthread_join(tid, NULL); // 阻塞等待指定线程退出回收资源pthread_detach(tid); // 非阻塞,让指定的线程为分离态,结束时自动回收资源// 主线程printf("in the main\n");while(1);return 0;}
练习:输入输出,quit结束

通过线程实现数据的交互,主线程循环从终端输入,线程函数将数据循环输出,当输入quit结束程序。

  1. 全局变量
  2. 加上标志位(flag),实现主线程输入一次修改标志位,从线程打印一次也修改标志位, int flag=0;
#include <stdio.h>#include <pthread.h>#include <unistd.h>#include <string.h>char buf[32] = {};int flag = 0;void *handler(void *arg){while (1){if (flag == 1){if (!strcmp(buf, "quit"))break;printf("%s\n", buf);flag = 0;}}return NULL;}int main(int argc, char const *argv[]){pthread_t tid;if (pthread_create(&tid, NULL, handler, NULL) != 0){perror("create thread err");}while (1){scanf("%s", buf);flag = 1;if (!strcmp(buf, "quit"))break;}pthread_join(tid, NULL);return 0;}

在应用层中有这两个概念:

同步:现在有两件事都是你应该去做的事情,那么这两件事是不是有先后的顺序

异步:我在做一件事情的同时还可以去做另一件事情

线程同步

概念:

多个线程(任务)按照约定的顺序相互配合完成一件事情

同步机制

通过信号量实现线程同步

信号量:通过信号量实现同步操作; 由信号量来决定线程继续运行还是阻塞等待。

信号量:代表一类资源,其值可以表示系统中该资源的数量。

信号量的值>0: 表示有资源可以用,可以申请到资源。

信号量的值<=0: 表示没有资源可以用,无法申请到资源,阻塞。

信号量:还是受保护的变量,只能通过三种操作来访问:初始化,P操作(申请资源),V操作(释放资源)

sem_init:信号量初始化

sem_wait:申请资源,P操作,如果没有资源可用则阻塞,否则就申请到资源 -1

sem_post:释放资源,V操作,非阻塞,+1

int sem_init(sem_t *sem, int pshared, unsigned int value)功能:初始化信号量参数:sem:初始化的信号量对象pshared:信号量共享的范围(0: 线程间使用 非0:1进程间使用)value:信号量初值返回值:成功 0失败 -1int sem_wait(sem_t *sem)功能:申请资源 P操作参数:sem:信号量对象返回值:成功 0失败 -1注:此函数执行过程,当信号量的值大于0时,表示有资源可以用,则继续执行,同时对信号量减1;当信号量的值等于0时,表示没有资源可以使用,函数阻塞int sem_post(sem_t *sem)功能:释放资源 V操作参数:sem:信号量对象返回值:成功 0失败 -1注:释放一次信号量的值加1,函数不阻塞
#include <stdio.h>#include <semaphore.h>int main(int argc, char const *argv[]){sem_t sem;if(sem_init(&sem, 0, 1) < 0){perror("sem init err");return -1;}// 申请资源 -> P操作sem_wait(&sem);printf("hello\n");// 释放资源sem_post(&sem);sem_wait(&sem); // 没有释放资源,会进入阻塞状态printf("world\n");return 0;}

线程互斥

互斥概念

多个线程访问临界资源时,同一时间只能一个线程访问

临界资源:多个线程共同访问的数据,且一次仅允许一个线程所使用的资源

通过互斥锁可以实现互斥机制,主要用来保护临界资源,每个临界资源都由一个互斥锁来保护,线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止。

互斥锁的操作方式:初始化

申请锁(上锁) :阻塞

当申请不到锁时(表示:锁被其它线程占用),是阻塞的

释放锁(解锁) :非阻塞

注意:上锁和解锁需要成对存在

函数接口

int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)功能:初始化互斥锁参数:mutex:互斥锁attr: 互斥锁属性 // NULL表示缺省属性返回值:成功 0失败 -1int pthread_mutex_lock(pthread_mutex_t *mutex)功能:申请互斥锁参数:mutex:互斥锁返回值:成功 0失败 -1注:和pthread_mutex_trylock区别:pthread_mutex_lock是阻塞的;pthread_mutex_trylock不阻塞,如果申请不到锁会立刻返回int pthread_mutex_unlock(pthread_mutex_t *mutex)功能:释放互斥锁参数:mutex:互斥锁返回值:成功 0失败 -1int pthread_mutex_destroy(pthread_mutex_t *mutex)功能:销毁互斥锁参数:mutex:互斥锁

补充:死锁

是指两个或两个以上的进程或线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

死锁产生的四个必要条件

1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用

2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。

3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。

  1. 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

注意:当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。

http://www.rkmt.cn/news/89178.html

相关文章:

  • Python列表与元组:搞懂这3个核心差异,再也不纠结用哪个
  • MQ消息队列相关知识与对比
  • 完整教程:PPT导出为图片的格式选择:JPG与PNG的区别
  • 代码随想录算法训练营第三十二天 | 完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ、卡码网57. 爬楼梯
  • 基于深度学习的文物图像修复系统
  • JavaScript 引擎中的分支预测器(Branch Predictor)友好性:如何写出减少 CPU 误判的代码
  • Day 37 - 早停策略与模型权重的保存
  • 【SOVD】软件定义汽车时代的诊断新范式
  • 最全词典整合收录:打造专业英语学习利器
  • C盘哪些文件可以删除?
  • 18、深入了解 Linux 文件系统:导航与分区指南
  • PLM系统更专业化:更适配汽车电子芯片半导体研发的高标准管理选择——全星研发项目管理APQP软件系统应用解析
  • 磁盘清理工具没反应怎么办
  • 从入门到转行:网络安全自学与跳槽的终极建议
  • PyTorch Geometric中TUDataset加载问题全解析:从诊断到实战
  • 12月12日总结 - 作业----
  • Blade构建系统终极指南:新手快速上手指南
  • Extreme Programming--front-end and back-end separation contacts programming
  • 终于交出焚诀了,运营新思路:短视频动漫化
  • 不缺席娃成长,也能过法考!宝妈备战法考秘籍,UU带你碎片化时间稳过线
  • 【Anthropic分享博客】Anthropic 内部的 Agentic Workflow 工程实践
  • 孤能子视角:“DeepSeek偏向中文思考“本质推测
  • 基于python大数据的的海洋气象数据可视化平台 - 详解
  • AutoHotkey鼠标轨迹自动化终极指南:从零开始实现精准操作回放
  • RobotStudio2025全功能授权
  • 如何快速搭建自动驾驶平台:开源汽车控制系统的完整指南
  • dotNetFx40_Full_x86_x64完整安装包:快速部署.NET Framework 4.0开发环境
  • Bruce Web界面终极指南:远程控制渗透测试设备的完整解决方案
  • NOIP2025反思——于诗涵
  • 完整教程:【MySQL】从零开始了解数据库开发 --- 数据表的索引