Linux的线程控制

Linux多线程工作笔记0003---C语言中%p,%u,%lu都有什么用处 技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152 %p表示输出这个指针, %d表示后面的输出类型为有符号的10进制整形, %u表示无符号10进制整型, %lu表示输出无符号长整型整数 (long unsigned) 阅读详情

进程两个特点:资源和调度,而线程是进程内的独立执行代码的实体和调度单元

进程内的线程共享进程的资源

线程间共享线程私有
进程指令
全局变量(非常容易实现线程的数据共享)
打开的文件
信号处理程序
当前工作目录
用户ID和组ID
线程ID
PC指针(标识当前线程的执行位置)
线程栈

线程控制

获取线程ID:pthread_t pthread_self(void)
        返回值:成功:返回线程ID; 失败:无!
        线程ID:pthread_t类型,本质:在Linux下为无符号整数(%lu)

线程的创建:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)
        返回值:
                成功:0; 失败:错误号 -----Linux环境下,所有线程特点,失败均直接返回错误号。
        参数:
                pthread_t:当前Linux中可理解为:typedef  unsigned long int  pthread_t;
                参数1:传出参数,保存系统为我们分配好的线程ID
                参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
                参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
                参数4:线程主函数执行期间所使用的参数,如要传多个参数, 可以用结构封装

线程的终止:int pthread_join(pthread_t thread, void **retval)
        返回值 : 0代表成功。 失败,返回的则是错误号。
        参数 :thread: 线程标识符,即线程ID,标识唯一线程。
                    retval: 用户定义的指针,用来存储被等待线程的返回值。

线程有三种终止方式:
        1、自己返回退出
        2、被同一进程中的其他线程请求取消(int pthread_cancel(pthread_t thread);)
        3、线程在任意函数中调用pthread_exit()函数执行终止(void pthread_exit(void *retval);)
        若A,B线程为同一进程中的两个线程,A调用pthread_cancel(B)等价于B调用pthread_exit()

进程与线程的对比

线程的数据共享

临界资源:在一段时间内只允许一个任务(线程/进程)访问的资源。

静态初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
动态初始化:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t*restrict attr)
        参数:mutex: 要初始化的互斥量的地址
                   attr: 互斥量的属性(一般设置为NULL)

加锁: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)

条件变量

多个线程访问临界资源有需要相互合作。如线程A先需要某种操作(修改全局变量X)后,线程B才能(根据全局变量X判断)执行另一操作。这就涉及到了线程执行中的条件变量
条件变量与互斥量一起使用时,允许线程以互斥的方式阻塞等待条件的发送(同步)

触发条件线程x:
        互斥量加锁 -> x操作-> 触发条件变量 -> 互斥量解锁
等待条件线程y:
        互斥量加锁 -> 等待条件变量-> y操作 -> 互斥量解锁
    
静态初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIZER
动态初始化:int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr)
        参数:cond: 一个指向结构pthread_cond_t的指针
                   cond_attr: 一个指向结构pthread_condattr_t的指针

等待条件:
阻塞条件变量:int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)
指定时间内阻塞条件:int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime)

销毁:int pthread_cond_destroy(pthread_cond_t *cond);  

触发条件变量:
        int pthread_cond_signal(pthread_cond_t *cond);    //唤醒等待的线程中的某一线程
        int pthread_cond_broadcast(pthread_cond_t *cond);  //唤醒等待的线程中的所有线程,这些线程进行竞争

代码实例1如下:

/**************************************
作   者 : lijd
生成日期 : 2020年12月29日
功能描述 : 两个线程对全局变量操作,两个条件:
          1、当count大于0时才能做减1操作
          2、当count小于等于0时只能做加1操作
**************************************/ 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
 
pthread_mutex_t count_lock;
pthread_cond_t count_ready;

int count = 0;

// 对全局变量做减1操作
void *dec(void *arg)
{
    printf("dec thread:%ld\n", pthread_self());
    pthread_mutex_lock(&count_lock);
    printf("des waiting\n");

    if(count <= 0)
    {
        pthread_cond_wait(&count_ready, &count_lock);    // 阻塞等待
    }
    
    count = count - 1;
    printf("des count = %d\n", count);
    pthread_mutex_unlock(&count_lock);
    printf("des quit!\n");
	
    pthread_exit(NULL);
}

// 对全局变量做加1操作
void *inc(void *arg)
{
    printf("inc thread:%ld\n", pthread_self());
	
    pthread_mutex_lock(&count_lock);
    printf("inc running\n");
	
    count = count + 1;
    printf("inc count = %d\n", count);

    if(count > 0)
    {
        pthread_cond_signal(&count_ready);    // 唤醒等待的线程中的某一线程
    }
	
    pthread_mutex_unlock(&count_lock);
    printf("inc quit!\n");
	
    pthread_exit(NULL);
}

int main(void) {
    pthread_t tid1,tid2;
	
    printf("main thread:%ld\n", pthread_self());
	
    pthread_mutex_init(&count_lock, NULL);
    pthread_cond_init(&count_ready, NULL);
	
    // 先创建tid1线程des函数
    pthread_create(&tid1, NULL, dec, NULL);
    sleep(2);
	
    // 后创建tid2线程inc函数
    pthread_create(&tid2, NULL, inc, NULL);
	
    printf("tid1:%ld, tid2:%ld\n", tid1, tid2);
	
    pthread_join(tid2, NULL);
	
    pthread_join(tid1, NULL);
	
    return 0;
}

执行结果如下:

读写锁

问题描述:
        在对临界资源的访问中,更多的是读操作,写操作较少,只有互斥量机制会影响访问效率。期望对临界资源的访问控制粒度更精细,从而引出读写锁。
        读写锁对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作

互斥关系:
        读操作 - 写操作 互斥
        写操作 - 写操作 互斥
        读操作 - 读操作 不互斥

同步关系:
        缓冲区不满,才允许写操作;缓冲区不空,才允许读操作。

访问控制规则:
        如果有线程对互斥资源进行读操作,则允许其他线程执行读操作,但不允许任何线程执行写操作;
        如果有线程对互斥资源进行写操作,则不允许任何线程执行读操作和写操作;

读写锁操作API:

静态初始化:pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER
动态初始化:int pthread_rwlock_init(pthread_rwlock_t *rwptr, const pthread_rwlockattr_t *attr)
        参数:rwptr: 要初始化的读写锁的地址
                   attr: 读写锁的属性(一般设置为NULL)

                   
销毁: int pthread_rwlock_destroy(pthread_rwlock_t *rwptr)

阻塞加读锁:int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
非阻塞加读锁:int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)

阻塞加写锁:int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
非阻塞加写锁:int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)

释放读锁或写锁:int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)

代码实例2如下:

/**************************************
作   者  : lijd
生成日期 : 2020年12月29日
功能描述 : 验证读写锁机制
**************************************/ 
#include <stdio.h>
#include <stdlib.h>
#include<pthread.h>
#include<string.h>
#include <sys/time.h>

#define WRNUM	2
#define RDNUM	2
#define STR_NUM 3
char test[STR_NUM] = "123";

// 定义全局读写锁
pthread_rwlock_t rwlock;

// 获取的是秒 (1秒 = 1000毫秒 =1000000微妙)
long getCurrentTime()  
{  
   struct timeval tv;  
   gettimeofday(&tv,NULL);
   return (tv.tv_sec * 1000 + tv.tv_usec/1000)/1000;  
}

void PthWrite()
{
    char w_str[] = "ABC"; 
    int i = 0;
    for(i = 0; i < STR_NUM; i++)
    {
        pthread_rwlock_wrlock(&rwlock);
        test[i] = w_str[i];
        printf("time:%lu, PthWrite ID:%lu, Write:%c, iNum:%u, Now Sleeping Start...\n",getCurrentTime(), pthread_self(), w_str[i], i);
        sleep(1);
        printf("time:%lu, PthWrite ID:%lu, iNum:%u, Now Sleeping End!\n",getCurrentTime(), pthread_self(), i);
        pthread_rwlock_unlock(&rwlock);
        sleep(1);
    }
	
	pthread_rwlock_destroy(&rwlock);
}

void PthRead()
{
    int i = 0;
    for(i = 0; i < STR_NUM; i++)
    {
        pthread_rwlock_rdlock(&rwlock);
        printf("time:%lu, PthRead ID:%lu, Read:%c, iNum:%u, Now Sleeping Start...\n",getCurrentTime(), pthread_self(), test[i], i);
        sleep(1);
        printf("time:%lu, PthRead ID:%lu, iNum:%u, Now Sleeping End!\n",getCurrentTime(), pthread_self(), i);
        pthread_rwlock_unlock(&rwlock);
        sleep(1);
    }
	
    pthread_rwlock_destroy(&rwlock);
}

int main()
{
    pthread_t rd[WRNUM], wr[RDNUM];
    int i;
	
    pthread_rwlock_init(&rwlock, NULL);
	
    for(i = 0; i < WRNUM; i++)
    {
        pthread_create(&wr[i], NULL, PthWrite, NULL);
    }
	
    for(i = 0; i < RDNUM; i++)
    {
        pthread_create(&rd[i], NULL, PthRead, NULL);
    }
	
    for(i = 0; i < WRNUM; i++)
    {
        pthread_join(wr[i], NULL);
    }
	
    for(i = 0; i < RDNUM; i++)
    {
        pthread_join(rd[i], NULL);
    }
	
    return 0;
}

执行结果如下:

Silvaco Atlas新手入门:从安装到第一个半导体器件仿真(附DeckBuild配置) 本文为Silvaco Atlas新手提供了一份从软件安装、DeckBuild环境配置到完成第一个半导体器件仿真的实战指南。通过构建一个经典的PN结二极管案例,详细讲解了Atlas仿真语句的语法、核心命令顺序以及结果调试与可视化方法,帮助初学者快速上手并理解TCAD仿真流程。 阅读详情

相关推荐

英飞凌TC397芯片时钟树详解:从EB-tresos配置到PLL锁相环实战避坑

本文详细解析了英飞凌TC397芯片的时钟树架构,从EB-tresos配置到PLL锁相环的实战应用,帮助工程师避免常见配置陷阱。文章深入探讨了时钟源层、频率转换层和分配网络层的设计原理,提供了PLL配置的数学公式和EB-tresos关键容器的配置要点,并分享了调试技巧与性能优化实践,适用于MCU开发中的时钟管理需求。

weixin_42524226的博客 212

FPGA数字系统设计(10)——数据通讯fifo

打

qq_24213087的博客 946

分享一下如何使用labelimg制作自己的YOLO数据集

首先准备一下自己的下准备一个空文件夹用于保存标签文件,如下图所示。

skywalkerxhx的博客 831

【Linux】线程控制

若每一个线程都有一个临时变量,则将所有的临时变量数据都放入地址空间的栈中,就会导致数据谁是谁的分不清楚。主线程打开一个文件,然后又创建了一个新线程,新线程可以看到主线程打开的文件,同样也可以读取文件描述符。覆盖后,并没有改变在上一个线程传递的起始地址,即name没有变化,但是内容为缓冲区更新后的。循环多少次,new就会执行多少次,随着每一次的new,传递给新线程的起始地址就只属于该线程。等待方式默认是阻塞的,若新线程没有退出, 主线程就一直等待,直到新线程退出。

qq_62939852的博客 1706

ollama接入图像识别大模型

ollama接入图像识别大模型

qq_36437991的博客 2075

高速电路的信号完整性分析

摘要:介绍了高速PCB设计中的信号完整性概念以及破坏信号完整性的原因,从理论和计算的层面上分析了高速电路设计中反射和串扰的形成原因,并介绍了IBIS仿真。 关键词:信号完整性 反射 串扰 IBIS仿真 随着半导体技术和深压微米工艺的不断发展,IC的开关速度目前已经从几十MHz增加到几百MHz,甚至达到几GHz。在高速PCB设计中,工程师经常会碰到误触发、阻尼振荡、过冲、欠冲、串...

bh_wang的专栏 1431

线程控制--Linux

线程控制重点掌握pthread_create、pthread_exit、pthread_cancel、pthread_join、pthread_detach这几个函数就可以

qq_63412763的博客 895

深度学习的卷积神经网络的煤矸石识别

在本博文中,我们提出了一种基于深度学习的煤矸石检测与识别系统,该系统采用YOLOv8算法对煤矸石进行检测和识别,实现对图片中的煤矸石进行准确识别。1.项目需求对煤矸石进行检测与识别,系统将识别出图片中的克服小块煤矸石漏检的缺陷。 2.项目实现 通过调研,本项目最终使用yolov8模型作为目标检测的模型, YOLOv8 旨在快速、准确且易使用,使其成为广泛的物体检测、图像分割和图像分类任务的极佳选择。 1.数据集:格式为jpg的3091张煤矸石图片2.YOLOv8环境配置yolo检测网络包括24个卷积层

2201_75355700的博客 792

Linux之多线程(下)——线程控制

本文介绍了Linux下的线程控制。

一只小卷毛的博客 1642

Fiori Elements 的 Adapting the UI: Analytical List Page 全解析与实战

摘要:本文详细介绍了如何通过SAP Fiori elements的UI Adaptation功能对Analytical List Page(ALP)进行细粒度适配。重点讲解了页头过滤区、图表区域、表格区域等关键组件的可配置属性,包括过滤区折叠/固定、图表类型限制、表格滚动阈值等实用功能。文章强调这些适配均可通过可视化工具完成,无需修改业务代码,并提供了典型场景的实践清单。适用于SAP Fiori elements for OData V2项目,建议配合SAP Fiori tools的Adaptation E

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 436

IEC61850规约客户端软件开发实战(第六章)

IEC 61850标准中的GOOSE(Generic Object Oriented Substation Event,面向通用对象的变电站事件)功能是变电站自动化系统的核心通信机制,主要用于实现智能电子设备(IED)间高可靠性、低延时的实时信号传输。本章简述其核心功能与技术特点。

半青年的博客 1778

[Linux]——Linux线程控制

Linux线程控制 上篇文章我们介绍了线程的基本概念,详细叙述了线程的一些细节性知识点,我们之前写过进程控制的博客,但是我们今天所写的关于线程控制的函数与进程控制有所不同。相信你肯定还记得我们上篇博客中说明了在Linux下实际上没有真正意义上的线程,他们都是利用进程模拟的,那么也就意味着系统并没有提供一组系统调用接口来进行线程的控制,所以我们先来谈谈使用什么方案进行线程控制。 POSIX线程库 因...

lucky52529的博客 856

创龙 瑞芯微 RK3588 国产2.4GHz八核 工业开发板—ISP图像处理开发案例

创龙科技研发的 TL3588-EVM 评估板具备强大视频处理能力,可满足多场景视频开发需求。为助力开发者快速上手,本文整理 7 个实用视频开发案例,涵盖从采集到显示的全流程,详细说明各案例的硬件连接、参数配置与代码解析,帮助开发者高效实现评估板视频相关功能开发与调试。

创龙,嵌入式一体化解决方案商 208

Linux多线程——线程控制

线程是进程内的基本执行单位,作为 CPU 执行的基本单位,线程的控制与任务执行效率息息相关。合理地进行线程管理,能大大提升程序的执行效率,掌握线程的基本操作至关重要。

2401_83603768的博客 1124

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

<Linux> 线程控制

关于线程资源的分配,以及线程控制的各个接口和实操,还有关于线程库的深入详解

weixin_61522065的博客 1386

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

【Linux系统】线程控制

POSIX线程(pthreads)是Linux系统实现的多线程编程标准,通过glibc库提供线程管理功能。核心内容包括:线程创建(pthread_create)需指定线程函数和参数,编译时需链接-lpthread;线程终止可通过return、pthread_exit或pthread_cancel实现;资源回收通过pthread_join(可连接线程)或pthread_detach(分离线程)完成。线程管理采用"先描述后组织"策略,通过线程控制块(TCB)维护线程状态,并与内核协作实现真正

2402_82670467的博客 2807

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Linux线程-概念和控制

Linux多线程-概念和控制零、前言一、Linux线程概念1、什么是线程2、vfork函数/pthread线程库3、线程优缺点及其他分析二、Linux进程VS线程1、进程和线程三、Linux线程控制1、POSIX线程库2、线程创建3、线程ID及线程地址空间布局4、线程终止5、线程等待6、线程分离 零、前言 本章主要讲解学习Linux中的线程 一、Linux线程概念 1、什么是线程 概念: 在一个程序里的一个执行路线就叫做线程(thread),更准确的定义是:线程是“一个进程内部的控制序列”

~ 2609

【Linux】线程控制函数

线程控制函数的使用以及扩展的线程内容:c++线程库和每个线程的独立栈结构

s_little_monster的博客 9019

Linux线程——线程控制及理解

本文介绍了Linux下线程控制的基本原理与验证方法。通过代码示例验证了三个关键结论:1)线程本质是轻量级进程,共享相同PID;2)线程共享进程地址空间;3)线程异常会导致整个进程退出。文章详细讲解了pthread线程库的作用,指出其在Linux系统中的重要性,并解释了为何需要动态链接(-lpthread)。同时对比了不同系统版本对线程库的支持差异,以及C++11线程库在不同平台下的实现原理。通过实验验证,帮助读者理解Linux线程实现机制及跨平台线程库的设计考量。

2301_79705195的博客 758

Linux线程控制

所以,我在自己的代码里面调用pthread_create,是在动态库内部帮我们创建动态库描述的控制块,管理块的开头叫TCB,里面包含了描述线程的相关信息,而TCB里包含了void *ret字段,而当前线程运行结束的时候,会把自己的退出状态写到自己的控制块ret里,然后主线程又和新线程共享地址空间,主线程也就能通过起始地址+偏移量找到对应线程的控制块,主线程通过pthread_join拿到对应的退出状态拷贝回来,进程结束LWP自动释放,但是库里的东西没被释放,LWP查不到内存泄漏。怎么理解这个“ID”呢?

2302_81171591的博客 1431

Linux 线程控制

而这些都是线程库在做的事情,也就是线程的概念是库给我们维护的,我们用的原生线程库,也要加载到内存中,因为都是基于内存的。我们的线程库只需要维护线程的概念即可,不用维护线程的执行流,不过线程库注定了要维护多个线程属性集合,线程也要管理这些线程,先描述在组织。这是因为新线程内部执行进程替换函数,这看起来像是把新线程中的代码替换了,但实际会把主线程中的代码也替换了,因为主线程和新线程共享地址空间,所以新线程内部进程替换后,所有的线程包括主线程都会被影响。参数是void*,和这个函数的返回值的含义是一样的。

puppy_1mo的博客 1396

【Linux】线程概念 | 线程控制

线程是在进程内部执行的,也就是说线程是在进程的地址空间内运行的,其是操作系统调度的基本单位。进程等于内核数据结构加上该进程对应的代码和数据,内核数据结构可能不止一个 PCB,进程是承担分配系统资源的基本实体,将资源分配给线程!那如何理解我们之前写的代码呢?其实我们之前学习的是只有一个执行流的进程,而今天学习的是具有多个执行流的进程(task_struct 是进程内部的一个执行流),所以这两者是不冲突的。

m0_46676283的博客 1407

Linux之线程控制

对页表的理解,对线程概念,线程ID和线程创建,线程终止,线程等待,线程分离进行了详细讲解!

. 2769
上一篇: Linux 缓冲区与fork()函数
下一篇: CPU亲和力
码农诗人
博客等级 码龄11年 987粉丝 84原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值