当前位置: 首页 > 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.cnnetsun.cn/news/27009.html

相关文章:

  • V助手舆情分析智能体:重塑舆情分析,从“人找信息”到“信息为人”
  • 连接2026:十款远程控制软件真实力横评与选择指南
  • 计算机毕业设计springboot基于Spark++Vue.js的学生管理系统 Spark+Vue 高校学生综合信息管理平台 基于 SpringBoot+Spark+Vue 的全链路学生事务中心
  • JavaScript 集合操作的哈希碰撞:攻击者如何利用特殊 Key 导致 Map/Set 性能降级到 O(N)
  • 为什么 C盘空间会莫名其妙减少(即使没装新软件)?
  • 17、深入理解 Linux 文件系统机制与结构
  • 29、Linux 软件使用与故障排除指南
  • 从入门到转行:网络安全自学与跳槽的终极建议
  • 网络安全小白自学之路,别拜师了,求人不如求己_网络安全小白怎么自学
  • 从系统运维到网络安全工程师,8个月转行真实经验分享!
  • 算法系列(Algorithm)- 快速排序
  • RobotStudio2025全功能授权
  • IsaacLab中UR机械臂与Robotiq夹爪的5大配置难点与解决方案
  • cmark Markdown解析器终极指南:从入门到精通
  • 4-bit量化FLUX模型:让专业AI绘图走进寻常百姓家
  • Excel VBA快速入门:7天从零到精通终极指南
  • AutoHotkey鼠标轨迹自动化终极指南:从零开始实现精准操作回放
  • UxPlay 终极指南:在 Linux 系统上实现 AirPlay 镜像的完整教程
  • 1-2 惜败!国安亚冠连败 中超 16 强魔咒难破
  • 一键解锁阅读3.0书源终极合集:1629个精品资源任你选
  • 一般人不懂Windows
  • Java 基于多线程机制的专项实验
  • 51、Linux 系统中 shell 环境管理与脚本编写全解析
  • 53、编写高效 Shell 脚本:从基础到实践
  • 解锁共享单车数据:从入门到精通的完整分析指南
  • KOOM:如何快速解决Android应用内存泄漏的终极方案
  • API 测试- Postman Vs Rest Assured
  • 如何在React Native应用中实现语音交互?
  • 突破创意瓶颈:BlenderMCP如何用AI重塑3D建模工作流
  • 生产环境出现问题,测试人如何做工作复盘?