线程:线程创建pthread_create,线程间的同步与互斥

news2024/11/12 17:24:51

线程的创建 

线程的创建是通过调用pthread_create函数来实现的。该函数的原型如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

参数说明:

  • thread:指向pthread_t类型的指针,用于存储新线程的ID。
  • attr:指向pthread_attr_t类型的指针,用于指定新线程的属性,可以为NULL。
  • start_routine:指向线程函数的指针,该函数的返回类型为void*,参数类型为void*。
  • arg:传递给线程函数的参数。

函数返回值:

  • 若线程创建成功,则返回0。
  • 若线程创建失败,则返回一个非零的错误码。

线程的退出 

退出线程:线程的退出是通过调用pthread_exit函数来实现的。该函数的原型如下:

void pthread_exit(void *value_ptr);

参数说明:

  • retval:指定的退出码。

使用该函数可以在不终止整个进程的情况下结束当前线程,并将退出码返回给等待它的线程。

该函数使当前线程退出,并将value_ptr指向的值作为退出状态返回给其他线程。可以将value_ptr参数设置为NULL,以表示退出状态不重要。线程在执行pthread_exit函数后,其资源会被系统自动回收。

线程的回收 

回收线程:线程的回收是通过调用pthread_join函数来实现的,pthread_join是一个函数,用于等待指定的线程终止。该函数的原型如下:

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

参数thread是要等待的线程的标识符,value_ptr是一个指向指针的指针,用于存储线程的返回值。

调用pthread_join函数将会阻塞调用线程,直到指定的线程终止。如果线程已经终止,pthread_join函数立即返回。如果线程还在运行,则调用线程将会等待,直到指定的线程终止。

如果线程的返回值不为NULL,则它将被存储在value_ptr指向的位置上。如果不关心线程的返回值,则可以将value_ptr参数设置为NULL。

pthread_join函数返回0表示成功,非零值表示失败。常见的失败情况包括:线程标识符无效或者线程已经被其他线程等待。

注意:在线程退出后,如果不进行回收操作,线程的相关资源可能会得不到释放,从而导致资源泄露。因此,建议在创建线程后,及时进行线程的回收操作。

线程的同步与互斥

1.互斥锁

在C语言中,使用线程互斥锁(Mutex)可以实现对共享资源的互斥访问,避免多个线程同时访问产生的竞争条件。下面是使用线程互斥锁的基本步骤:

  1. 包含头文件 pthread.h
#include <pthread.h>
  1. 定义一个互斥锁变量。
pthread_mutex_t mutex;
  1. 在需要保护的代码片段前后使用锁来进行加锁和解锁。
// 加锁
pthread_mutex_lock(&mutex);

// 保护的代码片段

// 解锁
pthread_mutex_unlock(&mutex);
  1. 在每个线程的入口函数中初始化互斥锁。
void* thread_func(void* arg) {
    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    // 其他操作
    
    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);

    return NULL;
}

需要注意的是,互斥锁的初始化和销毁只需要在程序的开始和结束阶段进行一次。在每次加锁和解锁操作前后,确保使用同一把互斥锁进行操作,以保证正确性。

另外,需要注意互斥锁只能保证同一进程内的线程互斥访问,对于不同进程间的线程需要使用其他的同步机制,如信号量或文件锁等。

2.线程同步之无名信号量

无名信号量是一种线程同步的机制,用于协调多个线程之间的执行顺序。在C语言中,可以使用pthread库中的信号量函数来实现无名信号量。

无名信号量包括两个主要的操作:P操作和V操作。P操作用于申请资源,V操作用于释放资源。当一个线程需要访问共享资源时,需要执行P操作来申请资源;当一个线程使用完共享资源后,需要执行V操作来释放资源。

3.线程同步之条件变量

在C语言中,条件变量是一种线程同步机制,它允许一个线程(或多个线程)等待另一个线程满足特定的条件后再继续执行。

条件变量通常与互斥量(mutex)一起使用,以确保在访问共享资源之前,只有一个线程能够进入关键区域。下面是使用条件变量的一般步骤:

  1. 定义条件变量和互斥量
pthread_cond_t cond;
pthread_mutex_t mutex;
  1. 初始化条件变量和互斥量
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
  1. 线程等待条件变量满足
pthread_mutex_lock(&mutex);
while (condition_not_met) {
    pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
  1. 满足条件后,通知等待的线程
pthread_mutex_lock(&mutex);
set_condition_true();
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);

在步骤3中,线程会进入等待状态,并释放互斥量。当条件变量满足时,其他线程可以通过调用pthread_cond_signal或pthread_cond_broadcast来通知等待的线程。

需要注意的是,条件变量的使用必须与互斥量配合使用,这是为了避免竞态条件(race condition)。在等待条件变量时,线程会自动释放互斥量,并在接收到通知后重新获取互斥量。

总的来说,条件变量是一种用于线程同步的机制,它提供了一种等待特定条件满足的方式,并在条件满足时通知等待的线程。

作业1:创建3个线程,一个子线程拷贝文件的前一半,一个子线程拷贝后一半文件,主线程回收子线程资源。

#include <myhead.h>

sem_t sem1,sem2,sem3;

pthread_t tid1,tid2,tid3;

int up_text()
{
 
	int fd1,fd2;
	char buff[1024];
	int len;
	fd1=open("./1.txt",O_RDONLY);
	if(fd1==-1)
	{
		perror("open");
		return -1;
	}
 
	len=lseek(fd1,0,SEEK_END);
	if(len==-1)
	{
		printf("拷贝失败\n");
		close(fd1);
	}
	lseek(fd1,0,SEEK_SET);
 
	read(fd1,buff,len/2);//读取1.txt上半内容,存入buff
 
	fd2=open("./2.txt",O_WRONLY | O_CREAT ,0664);
	if(fd2==-1)
	{
		perror("open");
		close(fd2);
		return -1;
	}
 
	write(fd2,buff,len/2);//将buff的内容写入2.txt
	printf("上文拷贝成功\n");
 
	close(fd1);
	close(fd2);
 
}
 
int down_text()
{
	int fd1,fd2;
	char buff[1024];
	int len;
	fd1=open("./1.txt",O_RDONLY);
	if(fd1==-1)
	{
		perror("open");
		return -1;
	}
 
	len=lseek(fd1,0,SEEK_END);
	if(len==-1)
	{
		printf("拷贝失败\n");
	}
	lseek(fd1,len/2,SEEK_SET);
 
	read(fd1,buff,len/2);//读取1.txt 下半内容,存入buff
 
	fd2=open("./2.txt",O_WRONLY | O_CREAT ,0664);
	if(fd2==-1)
	{
		perror("open");
		return -1;
	}
 
	lseek(fd2,len/2,SEEK_SET);
 
	write(fd2,buff,len/2);//将buff的内容写入2.txt
	printf("下文拷贝成功\n");
 
	close(fd1);
	close(fd2);
 
}

void *fun1(void *ggg)//主线程
{
   	sem_wait(&sem1);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
	sem_post(&sem3);
    pthread_exit(NULL);

}
void *fun2(void *ggg)//子线程:复制前一半
{
	sem_wait(&sem3);
	up_text();
   	sem_post(&sem2);
    pthread_exit(NULL);
}
void *fun3(void *ggg)//子线程:复制后一半
{
	sem_wait(&sem2);
	down_text();
 	sem_post(&sem1);
    pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
    
	sem_init(&sem1,0,0);
    sem_init(&sem2,0,0);
    sem_init(&sem3,0,1);

    if(pthread_create(&tid1,NULL,fun1,NULL)!=0)
    {
        perror("ptcreat1");
        return -1;
    }
    if(pthread_create(&tid2,NULL,fun2,NULL)!=0)
    {
        perror("ptcreat2");
        return -1;
    }
    if(pthread_create(&tid3,NULL,fun3,NULL)!=0)
    {
        perror("ptcreat3");
        return -1;
    }

    sem_destroy(&sem1);
 	sem_destroy(&sem2);
	sem_destroy(&sem3);   
    pthread_join(tid1,NULL);
    return 0;
}

作业二:使用无名信号量实现循环输出 春、夏、秋、冬。

#include <myhead.h>
//春夏秋冬
sem_t sem1,sem2,sem3,sem4;
void *fun1(void *ggg)
{
    while(1)
    {
        sem_wait(&sem4);//申请将sem3的 value-=1
        printf("春\t");
        fflush(stdout);
        sem_post(&sem1);//释放 sem2 的 value=1
    }
    pthread_exit(NULL);
}
void *fun2(void *ggg)
{
    while(1)
    {
        sem_wait(&sem1);//将sem2的value=0
        printf("夏\t");
        fflush(stdout);
        sem_post(&sem2);//释放sem1 value=1
    }
    pthread_exit(NULL);
}
void *fun3(void *ggg)
{
    while(1)
    {
        sem_wait(&sem2);
        printf("秋\t");
        fflush(stdout);
        sem_post(&sem3);
    }
    pthread_exit(NULL);
}
void *fun4(void *ggg)
{
    while(1)
    {
        sem_wait(&sem3);
        printf("冬\t");
        fflush(stdout);
        sem_post(&sem4);
    }
    pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
    pthread_t tid1,tid2,tid3,tid4;
    
    sem_init(&sem1,0,0);
    sem_init(&sem2,0,0);
    sem_init(&sem3,0,0);
    sem_init(&sem4,0,1);//信号量sem4给进程1资源
    if(pthread_create(&tid1,NULL,fun1,NULL)!=0)
    {
        perror("ptcreat1");
        return -1;
    }
    if(pthread_create(&tid2,NULL,fun2,NULL)!=0)
    {
        perror("ptcreat2");
        return -1;
    }
    if(pthread_create(&tid3,NULL,fun3,NULL)!=0)
    {
        perror("ptcreat3");
        return -1;
    }
    if(pthread_create(&tid4,NULL,fun4,NULL)!=0)
    {
        perror("ptcreat4");
        return -1;
    }
	sem_destroy(&sem1);
	sem_destroy(&sem2);
	sem_destroy(&sem3);          
	sem_destroy(&sem4);          
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    return 0;
}

作业三:互斥锁,无名信号量,条件变量再联系一遍。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

// 互斥锁
pthread_mutex_t mutex;
// 条件变量
pthread_cond_t cond;
// 无名信号量
sem_t sem;

void* thread_func1(void* arg) {
    // 等待信号量
    sem_wait(&sem);
    pthread_mutex_lock(&mutex);
    printf("线程1获取了互斥锁\n");
    // 发送条件信号
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
	return NULL;
}

void* thread_func2(void* arg) {
    pthread_mutex_lock(&mutex);
    // 等待条件变量
    pthread_cond_wait(&cond, &mutex);
    printf("线程2获取了条件变量信号\n");
    pthread_mutex_unlock(&mutex);
    // 释放信号量
    sem_post(&sem);
	pthread_exit(NULL);
    return NULL;
}

int main() 
{
    pthread_t thread1, thread2;

    // 初始化互斥锁、条件变量和信号量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    sem_init(&sem, 0, 1);

    // 创建线程
    pthread_create(&thread1, NULL, thread_func1, NULL);
    pthread_create(&thread2, NULL, thread_func2, NULL);

    // 等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // 销毁互斥锁、条件变量和信号量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    sem_destroy(&sem);

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2080172.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开源word文档相似度对比 软件WinMerge

WinMerge 官网下载 &#xff1a;GitHub - WinMerge/winmerge: WinMerge is an Open Source differencing and merging tool for Windows. WinMerge can compare both folders and files, presenting differences in a visual text format that is easy to understand and hand…

ros2_python编程_多个文件python打包_目录拷贝_解决import错误问题ModuleNotFoundError

1.问题 ros2 python编写程序, 有多个python文件 如何打包多个python文件?解决import错误问题如何打包 有python目录结构的工程 1.ros2 多个python文件示例 代码目录结构, gitee 在线代码 tree 7_multi_file_setup/ 7_multi_file_setup/ ├── file1.py ├── main_node.…

飞书怎么关联任意两段话

最近开始用飞书记文档&#xff0c;体验实在是非常的丝滑&#xff0c;对我来说感觉没有找到更好的竞品了。废话不多说&#xff0c;接下来简单介绍一下怎么关联任意两段话吧。 首先说明&#xff0c;关联可以单向&#xff0c;也可以双向。 直接举例。 我想要将蓝字关联到最下面的…

国标GB28181视频监控EasyCVR视频汇聚平台国标注册被陌生IP入侵如何处理?

GB28181国标/GA/T1400协议/安防综合管理系统EasyCVR视频汇聚平台能在复杂的网络环境中&#xff0c;将前端设备统一集中接入与汇聚管理。智慧安防/视频存储/视频监控/视频汇聚EasyCVR平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级…

Java基础(包装类)

文章目录 前言 一、包装类的概述 二、自动拆装箱 三、128陷阱&#xff08;面试重点&#xff09; 四、自动拆装箱例题分析 前言 该篇文章创作时参考查阅了如下文章 Java种的包装类 Java包装类&#xff08;自动拆装箱&#xff09; Java--自动拆箱/装箱/实例化顺序/缓存…

第三期书生大模型实战营之茴香豆工具实践

文章目录 基础任务作业记录1. 环境准备2. 模型准备3. 修改配置文件4. 知识库创建6. 启动茴香豆webui 基础任务 在 InternStudio 中利用 Internlm2-7b 搭建标准版茴香豆知识助手&#xff0c;并使用 Gradio 界面完成 2 轮问答&#xff08;问题不可与教程重复&#xff0c;作业截图…

IDEA2023版本创建SSM项目框架

按图中红色数字顺序&#xff0c;先点击Maven&#xff0c;设置该项目为maven构建管理的项目&#xff0c;然后点击create进行项目创建 配置该项目的相关maven信息&#xff0c;按下图顺序进入到maven配置页面后进行本地maven相关信息配置。 创建web模块依次按下图中顺序进行点击 配…

朴世龙院士团队《Global Change Biology 》精确量化全球植被生产力对极端温度的响应阈值!

本文首发于“生态学者”微信公众号&#xff01; 随着全球气候变暖的加剧&#xff0c;极端温度事件对陆地生态系统的影响日益显著。植被作为生态系统的重要组成部分&#xff0c;其生产力对温度变化的响应尤为敏感。然而&#xff0c;关于极端温度如何以及在何种程度上影响植被生产…

TCP三次握手过程详解

三次握手过程&#xff1a; 客户端视角&#xff1a; 1.客户端调用connect&#xff0c;开启计时器&#xff0c;发送SYN包&#xff0c;如果重传超时&#xff0c;认为连接失败 2.如果收到服务端的ACK&#xff0c;则进入ESTABLISHED状态 3.清除重传计时器&#xff0c;发送ACK&…

windows权限维持汇总

Windows 权限维持 一、文件层面 1&#xff09;attrib 使用 Attrib s a h r 命令 s&#xff1a;设置系统属性&#xff08;System&#xff09; a&#xff1a;设置存档属性&#xff08;Archive&#xff09; h&#xff1a;设置隐藏属性&#xff08;Hidden&#xff09; r&#…

深度学习基础--11个专题带你入门Pytorch上

目的 本专栏更新深度学习基础&#xff0c;包括pytorch、tensorflow的使用以及CNN、RNN、yolo、GAN、LSTM、Transformer等神经网络的理论基础 前言 Pytorch是最常用的深度学习框架&#xff0c;里面包含了大量关于构建神经网络及其运算的APIPytorch基础入门分为上下两篇文章&am…

基于事件总线EventBus实现邮件推送功能

什么是事件总线 事件总线是对发布-订阅模式的一种实现。它是一种集中式事件处理机制&#xff0c;允许不同的组件之间进行彼此通信而又不需要相互依赖&#xff0c;达到一种解耦的目的。 关于这个概念&#xff0c;网上有很多讲解的&#xff0c;这里我推荐一个讲的比较好的&#x…

光伏设计时要画出哪些模型?

在光伏系统的设计中&#xff0c;为了确保项目的顺利实施与高效运行&#xff0c;设计师需要绘制多种模型来综合考虑各种因素&#xff0c;包括参照物、障碍物以及楼顶配房等。这些模型不仅有助于预测光伏系统的发电效率&#xff0c;还能帮助规划最佳的安装布局&#xff0c;减少阴…

碎片笔记|Computer Journal 期刊投稿注意事项

前言&#xff1a;3月份把之前做的一篇工作转投到了computer journal&#xff0c;8月7号来信说我投稿的工作之前因为挂在arXiv上&#xff0c;因此和正常的投稿要求不太一致&#xff0c;需要更换投稿方式&#xff0c;编辑提供了两种选择如下。 The first choice is to keep your …

python 接口自动化测试中的高阶函数!

高阶函数简介 高阶函数是指接受函数作为参数或者返回函数作为结果的函数。在 Python 中&#xff0c;有许多内置的高阶函数&#xff0c;如 map, filter, reduce 等&#xff0c;它们可以极大地简化代码并提高代码的可维护性。 summer camp map 函数 map 函数接收一个函数和一个…

Jmeter下载、配置环境变量

Jmeter下载 下载地址&#xff1a;Apache JMeter - Download Apache JMeter 下载后无需安装&#xff0c;解压后即可使用。解压后目录如下 配置环境变量 JMETER_HOME 环境变量Path %JMETER_HOME%\bin 环境变量CLASSPATH %JMETER_HOME%\lib 验证是否配置成功 在cmd命令窗中 输入…

玄机又成国漫首创!IP与AI融合,凭实力火出圈

现在国漫越来越卷了&#xff0c;不仅卷制作质量&#xff0c;还卷各种花式联动。最近玄机科技和百度文库联合举办的AI漫画大赛圆满结束&#xff0c;这还是国内的IP第一次和AI技术融合&#xff0c;而且产出了不少好作品。下面就一起来看看吧&#xff01; 提到玄机科技&#xff0c…

机器人笛卡尔空间轨迹规划-直线差补和圆弧差补

上一文&#xff0c;我们讨论了三次多项式和五次多项式的差补算法&#xff0c;那么这边文章具体讨论一下笛卡尔空间轨迹规划的直线差补和圆弧差补。 步骤 &#xff08;1&#xff09;知道起始点和终止点的位姿&#xff0c;和速度信息。 &#xff08;2&#xff09;根据两点能确定一…

湖仓一体大数据平台:开启企业数据管理新时代(附Hudi案例)

湖仓一体大数据平台&#xff1a;开启企业数据管理新时代&#xff08;附Hudi案例&#xff09; 前言湖仓一体大数据平台 前言 在当今数字化浪潮汹涌澎湃的时代&#xff0c;数据如同企业发展的珍贵宝藏&#xff0c;而如何高效地挖掘、管理和利用这些宝藏&#xff0c;成为了企业在…

详细的Anaconda安装jupyter notebook与使用

jupyter notebook概念 Jupyter Notebook 是一种交互式计算环境&#xff0c;广泛用于数据分析、机器学习和编程学习等领域。 一、pip安装 打开 Anaconda Prompt 输入&#xff1a;pip install jupyter notebook pip install jupyter notebook 安装成功画面 输入命令&#xff1…