多线程编译

news2024/11/23 22:20:04

多线程与多进程一样,为了能同时执行多个任务

区别

多进程

创建子进程,子进程会拷贝父进程的数据段的所有内存

进程是资源的获取单位

每个进程完全独立运行

更加关注两个进程之间的通信问题

多线程

线程是进程的最小组成单位,每个进程至少有一个线程(main函数所在的线程,称为主线程)

创建一个线程,会和原来的进程共享一个内存

线程是资源的分配单位

线程并不完全独立,主线程如果结束运行,整个进程就结束了,该进程中的其他线程也会随之结束(除了主线程外,其他线程结束运行没有影响)

更加关注先后运行的问题

多线程中的名词解释

临界数据

多个线程都能访问的数据

临界区

多个线程都能使用的代码,访问临界数据

竞态

多个线程同时想要访问临界数据时

互斥

多个线程同时访问临界数据时,只有一个线程允许访问临界数据,其他所有线程不允许访问的形式

同步

在互斥的基础上,安排好多个线程之间的运行顺序,使得每个线程的运行顺序都可预测

异步

多线程准备工作

sudo apt-get install manpages-pl-dev manpages-posix-dev      //安装手册

创建并运行线程

pthread_create函数

函数原型:

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

调用形式:

void* task (void *arg)

{

}

pthread_t=id;

pthread_create(&id,0,task,0);

功能:

创建一个线程并运行该线程

参数thread:用来接收新创建的线程的id号的变量的指针

参数attr(属性attribute的缩写):线程的属性(需要传入一个attr属性并且是指针类型时,直接传入0,表示默认属性)

参数start_routine:函数指针(该参数需要一个函数名),传入的函数就是线程创建成功后区运行的函数(传入不同的函数,创建成功的线程就会运行不同的代码)

参数arg:本质上会在pthread_create函数内部,传递给start_routine这个函数指针的的参数

  • 注意:线程函数库不会自动链接.o文件,所以每次编译的时候,需要手动连接,在编译的最后加上“-lpthread”(-l:表示链接函数库。pthread:表示函数库的库名)

线程的资源回收

每开启一个线程,也是需要8kb的内存空间。所以如果一个线程结束,这8kb的资源也需要回收

pthread_join函数

函数原型:

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

调用形式:

void* retval;

pthread_join(id,&retval);

功能:

等待线程id为thread的线程结束运行,并回收其资源。效果wait和waitpid是一样的

参数thread:等待资源回收的线程id

参数retval:传入一个void**的指针,所以在pthread_join函数内部,被这个指针指向的void*的值要被修改,retval指向的void*数据,会在pthread_join函数内部,去接收start_routine函数的返回值

pthread_detach函数

将线程的属性设置为分离式属性,分离式属性的线程,会在所属的进程结束运行的时候自动回收资源

函数原型:

int pthread_detach(pthread_t thread);

调用形式:

pthread_detach(id);

功能:

将线程的属性设置为分离式属性

  • pthread_detach不会阻塞以等待线程结束运行

线程属性设置流程

  1. 创建一个线程属性的变量

pthread_attr_t attr;

  1. 初始化线程属性

pthread_attr_init函数

函数原型:

int pthread_attr_init(pthread_attr_t *attr);

调用形式:

pthread_attr_init(&attr);

功能:

初始化线程属性

线程属性和默认值

Detach state = PTHREAD_CREATE_JOINABLE

Scope = PTHREAD_SCOPE_SYSTEM

Inherit scheduler = PTHREAD_INHERIT_SCHED

Scheduling policy = SCHED_OTHER

Scheduling priority = 0

Guard size = 4096 bytes

Stack address = 0x40196000

Stack size = 0x201000 bytes

上述每个属性,都有一个对应的set和get函数去设置和获取属性

  1. 使用每个线程对应的set函数去设置线程的属性

线程如何结束运行

进程结束运行:

  1. main指向return
  2. 调用exit或_exit

线程结束运行:

  1. 线程入口函数(线程运行的那个函数)执行return
  2. 调用pthread_exit

函数原型:

void pthread_exit(void *retval);

调用形式:

pthread_exit(0);

功能:

立刻结束当前线程的运行

参数retval:实际上就是传递给当前线程正在运行的那个函数的返回值

  1. 调用pthread_cancel
    函数原型
int pthread_cancel(pthread_t thread);
    调用形式
pthread_cancel(id)
    功能描述:取消线程id为thread的线程的运行
    
注意:pthread_cancel只是登记需要结束的线程,登记之后其实是不会立刻结束的
只有当线程运行到下一个退出点的时候,才会退出运行
为了保证pthread_cancel调用之后,一定不会运行后面的代码
可以手动的测试,该线程是否被登记取消运行
  1. pthread_testcancel
void pthread_testcancel(void);
    功能描述:立刻测试当前线程是否被cancel,如果被cancel了则退出线程的运行
  1. pthread_setcanceltype
int pthread_setcanceltype(int type, int *oldtype);
    功能描述:设置当前线程,是否允许通过 pthread_cancel 取消运行
    参数 type:有以下选项:
       PTHREAD_CANCEL_ENABLE:
           允许通过cancel函数取消线程的运行,这个是默认选项

       PTHREAD_CANCEL_DISABLE
           胡允许通过cancel函数取消线程的运行
    参数 oldtype:
        用来记录在更改线程取消状态之前,线程原先的取消状态,如果不需要记录,则直接传0即可   
  1. pthread_setcancelstate
int pthread_setcanceltype(int type, int *oldtype);
    功能描述:设置当前线程,是否允许通过 pthread_cancel 取消运行
    参数 type:有以下选项:
       PTHREAD_CANCEL_ENABLE:
           允许通过cancel函数取消线程的运行,这个是默认选项

       PTHREAD_CANCEL_DISABLE
           胡允许通过cancel函数取消线程的运行
    参数 oldtype:
        用来记录在更改线程取消状态之前,线程原先的取消状态,如果不需要记录,则直接传0即可   

同步与互斥

为什么要互斥

互斥是为了保证多个线程同时运行时,只允许一个线程完整访问临界数据,从而达到保护临界数据的目的

逻辑上实现互斥效果

在一个线程彻底访问完临界数据之前,其他所有会访问同样的临界数据的线程都不能进行访问

逻辑上实现同步效果

在一个线程彻底访问完临界数据之后,其他所有会访问同样的临界数据的线程都不能进行访问

注意

上面所有代码,互斥使用都是自己写的逻辑实现。
我们自己写的互斥逻辑,在运行过程当中,本身就会本其他线程入侵,导致互斥失败

所以,我们在做互斥操作的时候,一定要保证互斥本身是完整的一套操作,不会被其他线程入侵

这样的操作,有一个名词叫做:"原子操作"

互斥锁

什么是互斥锁

互斥锁本身是一个数量为1的临界数据,但这个临界数据有原子性,在操作的过程中,不会被其他线程入侵。

互斥锁的工作原理

互斥锁本身是一个数量为1的临界数据

只要有一个线程获取互斥锁(上锁),此时互斥锁数量变为0,此时别的进程获取不了互斥锁(处于阻塞状态),直到第一个线程释放互斥锁(解锁),此时别的线程就可以获取互斥锁(阻塞解除)

互斥锁的操作流程

  1. 创建一个全局互斥锁变量
pthread_mutex_t mutex;
  1. 初始化互斥锁
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
    这种形式为:静态创建互斥锁
    优点就是:方便快捷
    缺点就是:无论静态创建几把互斥锁,内核里面实际上永远是同一把互斥锁
    所以一般不推荐静态创建

int pthread_mutex_init(pthread_mutex_t *mutex, const  pthread_mutexattr_t  *mutex‐
attr);
调用形式:
pthread_mutex_init(&mutex,0);
    功能描述:初始化互斥锁
    参数 mutex:准备初始化的互斥锁的地址
    参数 mutex-attr:传0,表示创建一把默认的互斥    
  1. 上锁/解锁(抢互斥锁/释放互斥锁)
int pthread_mutex_lock(pthread_mutex_t *mutex);
调用形式:
pthread_mutex_lock(&mutex);
    功能描述:上锁一把互斥锁
    参数mutex:准备上锁的互斥锁的指针
    
int pthread_mutex_unlock(pthread_mutex_t *mutex);
调用形式:
pthread_mutex_unlock(&mutex);
      功能描述:解锁一把互斥锁
      参数mutex:准备解锁的互斥锁指针
  1. 不再使用互斥锁需要销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
调用形式:
pthread_mutex_destroy(&mutex);

互斥锁实现互拆的模型

线程1:
while(1){
    pthread_mutex_lock(&mutex)
    访问临界资源
    访问临界资源
    ......
    pthread_mutex_unlock(&mutex)
}

线程2:
while(1){
    pthread_mutex_lock(&mutex)
    访问临界资源
    访问临界资源
    ......
    pthread_mutex_unlock(&mutex)
}

在所有线程访问临界数据的前后使用互斥锁锁住

互斥锁实现同步

互斥锁实现同步的模型

保证线程1先运行:

上锁线程2的互斥锁
线程1:
    while(1){
        上锁自己的互斥锁
        .............
        解锁下一个要运行的线程的互斥锁                                            
    }
    
线程2:
    while(1){
        上锁自己的互斥锁
        .............
        解锁下一个要运行的线程的互斥锁                                                    
    }
  
有几个线程参与同步,就需要几把互斥锁              
先上锁不运行的线程的互斥锁
每一个线程运行之前,再一次上锁自己的互斥锁
每一个线程运行结束之后,解锁下一个要运行的线程的互斥锁

练习 

创建2个线程

1#线程:负责文件IO向文件中写入数据

2#线程:从该文件中读取数据

使用互拆锁

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>

typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;

int a = 0;

pthread_mutex_t ma;                           //创建五个互斥锁
pthread_mutex_t mb;
pthread_mutex_t mc;
pthread_mutex_t md;
pthread_mutex_t me;

void* task1(void* arg){
	while(1){
		pthread_mutex_lock(&mb);
		printf("2\n");
		sleep(1);
		pthread_mutex_unlock(&mc);

	}
}

void* task2(void* arg){
	while(1){
		pthread_mutex_lock(&mc);
		printf("3\n");
		sleep(1);
		pthread_mutex_unlock(&md);
	}
}

void* task3(void* arg){
	while(1){
		pthread_mutex_lock(&md);
		printf("4\n");
		sleep(1);
		pthread_mutex_unlock(&me);
	}
}

void* task4(void* arg){
	while(1){
		pthread_mutex_lock(&me);
		printf("5\n");
		sleep(1);
		pthread_mutex_unlock(&ma);
	}
}
int main(int argc, const char *argv[])
{
	pthread_mutex_init(&ma,0);                  //初始化五个互斥锁,并把不需要的互斥锁上锁

	pthread_mutex_init(&mb,0);
	pthread_mutex_lock(&mb);

	pthread_mutex_init(&mc,0);
	pthread_mutex_lock(&mc);	

	pthread_mutex_init(&md,0);
	pthread_mutex_lock(&md);

	pthread_mutex_init(&me,0);
	pthread_mutex_lock(&me);


	pthread_t id1,id2,id3,id4;                   //创建四个线程
	pthread_create(&id1,0,task1,0);
	pthread_detach(id1);

	pthread_create(&id2,0,task2,0);
	pthread_detach(id2);  

	pthread_create(&id3,0,task3,0);
	pthread_detach(id3);

	pthread_create(&id4,0,task4,0);
	pthread_detach(id4);

	while(1){
		pthread_mutex_lock(&ma);
		printf("1\n");
		sleep(1);
		pthread_mutex_unlock(&mb);

	}
	return 0;
}

创建5个线程,使用互斥锁安排这五个线程同步运行:1234512345

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>

typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;

int a = 0;

pthread_mutex_t ma;                           //创建五个互斥锁
pthread_mutex_t mb;
pthread_mutex_t mc;
pthread_mutex_t md;
pthread_mutex_t me;

void* task1(void* arg){
	while(1){
		pthread_mutex_lock(&mb);
		printf("2\n");
		sleep(1);
		pthread_mutex_unlock(&mc);

	}
}

void* task2(void* arg){
	while(1){
		pthread_mutex_lock(&mc);
		printf("3\n");
		sleep(1);
		pthread_mutex_unlock(&md);
	}
}

void* task3(void* arg){
	while(1){
		pthread_mutex_lock(&md);
		printf("4\n");
		sleep(1);
		pthread_mutex_unlock(&me);
	}
}

void* task4(void* arg){
	while(1){
		pthread_mutex_lock(&me);
		printf("5\n");
		sleep(1);
		pthread_mutex_unlock(&ma);
	}
}
int main(int argc, const char *argv[])
{
	pthread_mutex_init(&ma,0);                  //初始化五个互斥锁,并把不需要的互斥锁上锁

	pthread_mutex_init(&mb,0);
	pthread_mutex_lock(&mb);

	pthread_mutex_init(&mc,0);
	pthread_mutex_lock(&mc);	

	pthread_mutex_init(&md,0);
	pthread_mutex_lock(&md);

	pthread_mutex_init(&me,0);
	pthread_mutex_lock(&me);


	pthread_t id1,id2,id3,id4;                   //创建四个线程
	pthread_create(&id1,0,task1,0);
	pthread_detach(id1);

	pthread_create(&id2,0,task2,0);
	pthread_detach(id2);  

	pthread_create(&id3,0,task3,0);
	pthread_detach(id3);

	pthread_create(&id4,0,task4,0);
	pthread_detach(id4);

	while(1){
		pthread_mutex_lock(&ma);
		printf("1\n");
		sleep(1);
		pthread_mutex_unlock(&mb);

	}
	return 0;
}

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

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

相关文章

代码随想录算法训练营Day32 | 56. 合并区间 | 738.单调递增的数字 | 968.监控二叉树

今日任务 56. 合并区间 题目链接&#xff1a; https://leetcode.cn/problems/merge-intervals/题目描述&#xff1a; Code class Solution { public:vector<vector<int>> merge(vector<vector<int>>& intervals) {ranges::sort(intervals, [&…

Spring:springboot集成jetcache循环依赖问题

springboot版本&#xff1a;2.6.14 jetcache版本&#xff1a;2.6.2 启动项目报错如下&#xff1a; 解决方案&#xff1a; jetcache版本升级到2.6.4 https://github.com/alibaba/jetcache/issues/624

IT运维岗适用的6本证书

作为IT从业人员&#xff0c;不断提升自身的专业技能和知识是提升职场竞争力、助力升职加薪的重要途径。特别是在运维领域&#xff0c;虽然工作看似简单&#xff0c;但实际上需要掌握的技术知识却相当全面。为了全面提升自己的技术能力&#xff0c;并证明自己的专业能力&#xf…

每周心赏|七夕这样玩也太超前了吧,速来AI一下!

明天就是七夕节了&#xff0c;是时候给七夕节来点大震撼了&#xff0c;AI带你玩点不一样的&#xff01; 给大家挖掘了几个有梗又有爱的智能体。信我&#xff0c;快来试玩&#xff01; 不知道大家是什么人&#xff1f;反正&#xff0c;我是一个很爱测评的人&#x1f92d;&#…

【GaussDB(DWS)】数仓部署架构与物理结构分析

数仓架构与物理结构分析 一、部署架构二、物理结构三、测试验证 一、部署架构 华为数据仓库服务DWS&#xff0c;集群版本8.1.3.x 集群拓扑结构&#xff1a; 上述拓扑结构为DWS单AZ高可靠部署架构&#xff0c;为减少硬件故障对系统可用性的影响&#xff0c;建议集群部署方案遵…

制造企业技术图纸不受控的影响与规避方法

在制造企业中&#xff0c;技术图纸是产品设计、制造与检验的核心依据。若技术图纸不受控&#xff0c;将对企业造成诸多不利影响。 首先&#xff0c;产品质量无法得到保障。不受控的图纸可能存在设计缺陷、尺寸误差或工艺不合理等问题&#xff0c;导致生产出的产品不合格&#…

独辟蹊径:用Python打造你的副业帝国,迈向财富自由

在当今这个数字化时代&#xff0c;掌握一门编程语言如同拥有了一把开启无限可能的钥匙。Python&#xff0c;以其简洁的语法、强大的库支持和广泛的应用领域&#xff0c;成为了许多人实现副业收入乃至财富自由的首选工具。本文将探讨如何利用Python技能开启副业&#xff0c;并逐…

mysql中的表查询操作

performance_schema 系统数据库用于收集Mysql服务器的性能参数&#xff0c;以便数据库管理员了解产生性能瓶颈的原因。information_schema 系统数据库定义了所有数据库对象的元数据信息。 表的常规操作&#xff08;增删改查&#xff09; 我们经常对表进行以下操作 插入&#x…

OceanMind海睿思受邀参加第41届CCF中国数据库学术会议

CCF 中国数据库学术会议始于1977年&#xff0c;是由数据库专业委员会举办的中国数据库领域的最高学术会议&#xff0c;第41届中国数据库学术会议&#xff08;NDBC 2024&#xff09;将于2024年8月7日-8月10日在新疆乌鲁木齐举行。中新赛克副总兼大数据产品线总经理卢云川先生受邀…

匹配格值的前半部分

Excel有多列含空格的源数据&#xff0c;如C3:D19&#xff1b;还有若干用于比较的数据项&#xff0c;由"-"隔为前后两部分&#xff0c;如F3:F7。 要求用源数据的每列与数据项的前半部分进行比较&#xff0c;将匹配上的数据项填在该列下面。 使用 SPL XLL spl("d…

DALL•E 3 重新定义图像生成的人工智能

在人工智能的不断发展中&#xff0c;图像生成技术一直是一个备受关注的领域。OpenAI 的 DALL-E 系列自发布以来&#xff0c;便因其卓越的图像生成能力而备受瞩目。作为这一系列的最新成员&#xff0c;DALL-E 3 再次突破了技术的界限&#xff0c;为图像生成带来了全新的可能性。…

嵌入式day23

实现minishell minishell功能&#xff1a; 1,cp 复制文件 cp 1 2 把文件1复制成文件2 2,cat 查看文件 cat 1 查看文件到内容 3,cd 切换路径 cd 1 切换到目录1中 4,ls 查看当前目录下到文件 ls 或 ls /home 5,ll 查看当前目录下到文件 ll 或 ll /home 6,ln -s 创建软链接…

《加密与解密 》第四版读书笔记2

1、注入技术 1.1注入技术的背景 在Windows操作系统中&#xff0c;各个进程的内存空间是相互独立的&#xff0c;虽然能通过函数VirtualQueryEx/VirtualProtectEx查询、设置目标进程的内存信息和页属性&#xff0c;通过函数ReadProcessMemory/WriteProcessMemory对目标进程的内存…

第八天 排行榜功能

排行榜分析&#xff1a; 榜单分为两类&#xff1a; 实时榜单&#xff1a;也就是本赛季的榜单 历史榜单&#xff1a;也就是历史赛季的榜单 之前一个积分记录明细表 要知道&#xff0c;每个用户都可能会有数十甚至上百条积分记录&#xff0c;当用户规模达到百万规模&#xff…

C#调用c++的dll方法,动态调用c++dll的方法

文章目录 一、创建c的dll1.新建项目2.删除vs自建的.cpp和.h文件3.新建Algorithm.h和Algorithm.cpp4.编译c1.编译2.解决报错3.再次编译可以看到已经成功。4.查看成功输出的dll。 二、创建c#项目1.创建一个console控制台程序。2.把dll拷贝到c#生成的程序根目录。3.在c#的program.…

WPF自定义控件的应用(DynamicResource的使用方法)

1 DynamicResource的使用方法 可以在字典文件 的抬头区写入数&#xff1a; <SolidColorBrush x:Key"PrimaryBackgroundColor" Color"#FFABAdB3"/><SolidColorBrush x:Key"TextBox.MouseOver.Border" Color"#FF7EB4EA"/>&l…

得-物任务脚本

得某物任务脚本 该脚本主要用于自动化执行“得物”APP中的一些日常任务和活动&#xff0c;包括签到、任务完成、奖励领取等操作。使用了多个第三方库来加密、签名和发送请求。 任务的定时执行 脚本通过定时任务&#xff08;cron&#xff09;设置在每天的11:10执行。使用的依…

通用人工智能的中国道路

朱松纯 北京大学智能学院院长 北京大学人工智能研究院院长 《为机器立心》、《为人文赋理》 1 什么是“人”——解构人文的认知架构与UV理论 从物体&#xff0c;到生命体、智能体、智人。。。 生命度&#xff08;Animacy&#xff09;&#xff1a;物体与智能体的边界&#xff0…

LLC数字控制TMS320F28034,4-DSP的epwm配置介绍

LLC数字控制TMS320F28034&#xff0c;4-DSP的epwm配置介绍 1 TMS320F280341.1 概述1.2 PWM详细介绍 2 TMS320F28034 PWM功能框图2.1 ePWM功能模块2.2 ePWM功能寄存器框图 3 TMS320F28034 PWM初始化流程4 结合项目设计5 代码设计5.1 PWM初始化程序5.2 工程代码 6 总结 配套代码示…

OpenCV图像滤波(9)getGaussianKernel()函数的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 功能描述 cv::getGaussianKernel() 是 OpenCV 中的一个函数&#xff0c;用于生成一维高斯核。这种核通常用于实现高斯模糊滤波器&#xff0c;该滤波器可以…