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

14.Linux线程(2)线程同步、线程安全、线程与fork

        线程同步指的是当一个线程在对某个临界资源进行操作时,其他线程都不可以对这个资源进行操作,直到该线程完成操作,其他线程才能操作,也就是协同步调,让线程按预定的先后次序进行运行。线程同步的方法有四种:互斥锁、信号量、条件变量、读写锁。

1.互斥锁

 #include <pthread.h>int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);4.int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex);int pthread_mutex_destroy(pthread_mutex_t *mutex);

        示例代码如下,主线程和函数线程模拟访问打印机,主线程输出第一个字符‘a’表示开始使用打印机,输出第二个字符‘a’表示结束使用,函数线程操作与主线程相同。(由于打印机同一时刻只能被一个线程使用,所以输出结果不应该出现 abab):

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>pthread_mutex_t mutex;void * thread_fun(void* arg)
{int i = 0;for( ;i < 5; i++ ){pthread_mutex_lock(&mutex);write(1,"B",1);int n = rand() % 3;sleep(n);write(1,"B",1);pthread_mutex_unlock(&mutex);n = rand() % 3;sleep(n);}pthread_exit(NULL);
}int main()
{pthread_t id;pthread_mutex_init(&mutex,NULL);pthread_create(&id,NULL, thread_fun,NULL);36.int i = 0;for( ;i < 5; i++ ){pthread_mutex_lock(&mutex);write(1,"A",1);int n = rand() % 3;sleep(n);write(1,"A",1);pthread_mutex_unlock(&mutex);n = rand() % 3;sleep(n);}pthread_join(id,NULL);pthread_mutex_destroy(&mutex);53.exit(0);
}

2.信号量

 #include <semaphore.h>int sem_init(sem_t *sem, int pshared, unsigned int value);4.int sem_wait(sem_t *sem);int sem_post(sem_t *sem);int sem_destroy(sem_t *sem);

示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>
char buff[128] = {0};sem_t sem1;
sem_t sem2;void* PthreadFun(void *arg)
{int fd = open("a.txt", O_RDWR | O_CREAT, 0664);assert(fd != -1);//函数线程完成将用户输入的数据存储到文件中while(1){sem_wait(&sem2);if(strncmp(buff, "end", 3) == 0){break;}write(fd, buff, strlen(buff));memset(buff, 0, 128);sem_post(&sem1);}sem_destroy(&sem1);sem_destroy(&sem2);}int main()
{sem_init(&sem1, 0, 1);sem_init(&sem2, 0, 0);pthread_t id;int res = pthread_create(&id, NULL, PthreadFun, NULL);assert(res == 0);//主线程完成获取用户数据的数据,并存储在全局数组 buff 中while(1){sem_wait(&sem1);printf("please input data: ");fflush(stdout);fgets(buff, 128, stdin);buff[strlen(buff) - 1] = 0;sem_post(&sem2);if(strncmp(buff, "end", 3) == 0){break;}}pthread_exit(NULL);
}

3.  条件变量

        条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待这个共享数据的线程。

 #include <pthread.h>int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);int pthread_cond_signal(pthread_cond_t *cond); //唤醒单个线程int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒所有等待的线程int pthread_cond_destroy(pthread_cond_t *cond);

示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>pthread_mutex_t mutex;
pthread_cond_t cond;
void * fun1(void * arg)
{char* s = (char*)arg;while( 1 ){//阻塞,被唤醒pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);19. pthread_mutex_unlock(&mutex);printf("fun1 read:%s\n",s);if (strncmp(s,"end",3) == 0 ){break;}}}void * fun2(void * arg)
{char* s = (char*)arg;while( 1 ){//阻塞,被唤醒pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);pthread_mutex_unlock(&mutex);printf("fun2 read:%s\n",s);if ( strncmp(s,"end",3) == 0 ){break;}}}int main()
{pthread_t id[2];char buff[128] = {0};pthread_cond_init(&cond,NULL);pthread_mutex_init(&mutex,NULL);pthread_create(&id[0],NULL,fun1,(void*)buff);58. pthread_create(&id[1],NULL,fun2,        (void*)buff);while( 1 ){fgets(buff,128,stdin);if ( strncmp(buff,"end",3) == 0 ){pthread_mutex_lock(&mutex);pthread_cond_broadcast(&cond);pthread_mutex_unlock(&mutex);break;}else{pthread_mutex_lock(&mutex);pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);}}pthread_join(id[0],NULL);
pthread_join(id[1],NULL);
exit(0);
}

执行的结果如下:

4 读写锁

 #include <pthread.h>int pthread_rwlock_init(pthread_rwlock_t *rwlock, pthread_rwlockattr_t *attr);int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

5 线程安全

        线程安全即就是在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是一样的、正确的。那么就说这些线程是安全的。要保证线程安全需要做到:
1) 对线程同步,保证同一时刻只有一个线程访问临界资源。
2)在多线程中使用线程安全的函数(可重入函数),所谓线程安全的函数指的是:如果一个
函数能被多个线程同时调用且不发生竟态条件,则我们程它是线程安全的。

不保证线程安全的示例代码:

 #include <stdio.h>#include <stdlib.h>#include <assert.h>#include <unistd.h>#include <string.h>#include <pthread.h>void* PthreadFun(void *arg)
{char buff[] = "a b c d e f g h i";char *p = strtok(buff, " ");while(p != NULL){printf("fun:: %c\n", *p);p = strtok(NULL, " ");sleep(1);}
}int main()
{pthread_t id;int res = pthread_create(&id, NULL, PthreadFun, NULL);assert(res == 0);char buff[] = "1 2 3 4 5 6 7 8 9";char *p = strtok(buff, " ");while(p != NULL){printf("main:: %c\n", *p);p = strtok(NULL, " ");sleep(1);}
}

执行结果如下:保证线程安全的示例代码:

 #include <stdio.h>#include <stdlib.h>#include <assert.h>#include <unistd.h>#include <string.h>#include <pthread.h>void* PthreadFun(void *arg)
{char buff[] = "a b c d e f g h i";char *q = NULL;char *p = strtok_r(buff, " ", &q);while(p != NULL){printf("fun:: %c\n", *p);p = strtok_r(NULL, " ", &q);sleep(1);}}int main()
{pthread_t id;int res = pthread_create(&id, NULL, PthreadFun, NULL);assert(res == 0);char buff[] = "1 2 3 4 5 6 7 8 9";char *q = NULL;char *p = strtok_r(buff, " ", &q);while(p != NULL){printf("main:: %c\n", *p);p = strtok_r(NULL, " ", &q);sleep(1);}
}

执行结果如下:


6 线程与 fork

多线程中某个线程调用 fork(),子进程会有和父进程相同数量的线程吗?

        ​​子进程不会继承父进程的所有线程​​。当多线程程序的某个线程调用 fork()时,子进程仅会复制调用 fork()的线程本身,其他线程在子进程中会被彻底销毁。子进程成为一个单线程进程,仅包含调用 fork()的线程的副本。这是因为 fork()仅复制调用线程的执行状态(如栈、寄存器等),而其他线程的状态无法被完整复制到子进程,否则会导致资源冲突或未定义行为(如其他线程可能持有锁或处于中间状态)。因此,子进程的线程数量恒定为 1,无论父进程有多少线程。

父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁?

        ​​父进程中已加锁的互斥锁,在子进程中仍处于加锁状态​​。fork()会复制父进程的整个内存空间,包括互斥锁的锁定状态。如果父进程的某个线程在调用 fork()前锁定了互斥锁,子进程会继承该锁的锁定状态(即锁被持有),但​​子进程无法解锁它​​。原因在于:

  1. ​​锁的持有者丢失​​:锁的持有者(父进程中被销毁的线程)在子进程中不存在,导致锁永远无法被释放。

  2. ​​死锁风险​​:若子进程尝试对已锁定的互斥锁加锁(如调用 pthread_mutex_lock),会立即死锁,因为锁的持有者已消失。

        解决方案

        使用 pthread_atfork()注册处理函数,在 fork()前解锁所有互斥锁,确保子进程从一致状态开始运行。例如:

void pre_fork() { pthread_mutex_unlock(&mutex); }
void post_fork_parent() { pthread_mutex_lock(&mutex); }
void post_fork_child() { /* 子进程无需操作 */ }pthread_atfork(pre_fork, post_fork_parent, post_fork_child);

        这会在 fork()前解锁,父进程在 fork()后重新加锁,子进程则从解锁状态开始。

http://www.dtcms.com/a/338227.html

相关文章:

  • 【秋招笔试】2025.08.17大疆秋招机考第一套
  • plantsimulation知识点25.8.18-从一个RGV到另一台RGV,工件长度和宽度方向互换
  • pytest测试框架之基本用法
  • GPT-5之后:当大模型更新不再是唯一焦点
  • 本地搭建dify+deepseek智能体
  • 【unitrix数间混合计算】3.1 零标记trait(zero.rs)
  • 【最后203篇系列】033 Mongo副本集修复过程
  • Maven resources资源配置详解
  • 小程序被爬虫攻击,使用waf能防护吗?
  • Vision Master的C#脚本与opencv联合编程
  • 【opencv-Python学习笔记(7):图像平滑处理】
  • 【图像算法 - 17】慧眼识“果”:基于深度学习与OpenCV的苹果智能检测系统(附完整代码)
  • sqli-labs通关笔记-第54关 GET字符型注入(单引号闭合 限制10次探测机会)
  • 英特尔公司Darren Pulsipher 博士:以架构之力推动政府数字化转型
  • 【leetcode】392. 判断子序列
  • 【yocto】为什么要选择yocto?
  • leetcode4_452 and 763
  • 力扣热题100------19.删除链表的倒数第N个结点
  • 【MongoDB与Python:数据库操作入门教程】
  • 力扣hot100:移动零问题的巧妙解决:双指针与原地交换策略(283)
  • ETL中统一调度的方式
  • Vue深入组件:组件事件详解1
  • 如何使用 React 101 的 Highcharts 包装器
  • 【网络安全实验报告】实验七:简单的防火墙搭建实验
  • css word-pass
  • 数据转换细节揭秘:ETL如何精准映射复杂业务逻辑
  • 专题:2025AI技术应用与发展报告|附600+份报告PDF、数据仪表盘汇总下载
  • 前端处理导出PDF。Vue导出pdf
  • GPT-5博士级AI使用教程及国内平替方案
  • 活到老学到老之vue-vben-admin项目安装