线程相关概念

线程概念

线程是操作系统中一种基本的执行单元,是程序的最小调度单位。一个程序可以包含多个线程,每个线程代表一个独立的执行路径,使得程序可以并发地处理多个任务。

线程的基本概念
线程与进程的区别:

进程是资源分配的单位,每个进程拥有独立的内存空间、文件描述符、全局变量等资源。进程之间隔离较好,但创建和切换成本较高。
线程是调度的基本单位,同一进程中的线程共享进程的资源(如地址空间、文件描述符等),但拥有独立的栈、寄存器和程序计数器。线程间切换开销较低,适合并发处理。
线程的组成部分: 每个线程包含以下关键部分:

栈:用于存储线程的局部变量和函数调用栈。
程序计数器(PC):记录线程的当前执行位置。
寄存器:保存线程的运行状态。
线程本地存储(TLS):线程间独立的数据存储。
线程的优点:

并发性:在多线程环境下,程序可以同时执行多个任务,提升执行效率。
资源共享:同一进程内的线程可以直接访问共享内存、全局变量等资源,避免了进程间通信的开销。
低开销:线程的创建和上下文切换开销比进程低,使多线程适合 I/O 密集型和高并发任务。

线程共享的资源

线程共享的资源
地址空间:包括代码段、数据段、堆空间等,使线程间可以直接访问彼此的全局变量和堆内存。
文件描述符表:所有线程共享文件和网络连接,能共同读写相同的文件。
信号处理:大多数信号处理器是共享的,一些信号会影响整个进程的所有线程。
当前工作目录和环境变量:线程共享进程的当前工作目录和环境变量,任何改动会影响所有线程。
用户 ID 和组 ID:所有线程共享进程的用户 ID(UID)和组 ID(GID)。
线程独立的资源
栈:每个线程有独立的栈空间,用于存储各自的函数调用和局部变量,避免相互干扰。
寄存器:每个线程独立拥有寄存器状态(包括程序计数器和栈指针等),确保在线程切换时能正确恢复。
线程本地存储(TLS):每个线程可以有独立的线程局部变量(Thread-local variables),即便变量名称相同也不冲突。
信号掩码:每个线程有独立的信号掩码,控制各线程可以接受或阻塞哪些信号。
优先级:每个线程可以设置自己的优先级,影响调度顺序。
在多线程环境中,errno 不是线程共享的,而是 线程局部变量(Thread-Local Storage, TLS)。这意味着每个线程都有自己的 errno 值,彼此独立,互不干扰。

线程创建 – pthread_create

#include <pthread.h>
int pthread_create(pthread_t *thread, const
pthread_attr_t *attr, void *(*routine)(void *), void *arg);

成功返回0,失败时返回错误码
thread 线程对象
attr 线程属性,NULL代表默认属性
routine 线程执行的函数
arg 传递给routine的参数 ,参数是void * ,注意传递参数格式,

线程结束 – pthread_exit

#include <pthread.h>
void pthread_exit(void *retval);

结束当前线程
retval可被其他线程通过pthread_join获取
线程私有资源被释放

线程查看tid函数

pthread_t pthread_self(void) 查看自己的TID
#include <pthread.h>
pthread_t pthread_self(void);

线程回收 – pthread_join

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
对于一个默认属性的线程 A 来说,线程占用的资源并不会因为执行结束而得到释放
成功返回0,失败时返回错误码
thread 要回收的线程对象
调用线程阻塞直到thread结束
*retval 接收线程thread的返回值

线程分离pthead_detach

int pthread_detach(pthread_t thread); 成功:0;失败:错误号
指定该状态,线程主动与主控线程断开关系。线程结束后(不会产生僵尸线程)
pthread_attr_t attr; /通过线程属性来设置游离态(分离态)/

设置线程属性为分离
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

取消一个线程

int pthread_cancel(pthread_t thread); 杀死一个线程
void pthread_testcancel(void); //手动加入一个取消点

int pthread_setcancelstate(int state, int *oldstate);
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE

int pthread_setcanceltype(int type, int *oldtype);
PTHREAD_CANCEL_DEFERRED
PTHREAD_CANCEL_ASYNCHRONOUS

线程的清理

void pthread_cleanup_push(void (*routine) (void *), void *arg)
void pthread_cleanup_pop(int execute)

线程的同步和互斥

互斥机制
mutex互斥锁 读写锁
任务访问临界资源前申请锁,访问完后释放锁

互斥锁

互斥锁(Mutex,Mutual Exclusion)是一种线程同步机制,用于确保在同一时刻只有一个线程能访问或修改共享资源,以避免多线程并发访问共享资源时引起数据不一致或竞争条件。

互斥锁初始化 – pthread_mutex_init

动态初始化
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t * attr);
成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
attr 互斥锁属性,NULL表示缺省属性
man 函数出现 No manual entry for pthread_mutex_xxx解决办法
apt-get install manpages-posix-dev

静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //初始化互斥量

互斥锁销毁 pthread_mutex_destroy
申请锁 – pthread_mutex_lock

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex)

成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
pthread_mutex_lock 如果无法获得锁,任务阻塞
pthread_mutex_trylock 如果无法获得锁,返回EBUSY而不是挂起等待

释放锁 – pthread_mutex_unlock

#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);

成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
执行完临界区要及时释放锁

读写锁

读写锁(Read-Write Lock)是一种线程同步机制,允许多个线程同时读取共享资源,但在写入时,只有一个线程可以独占访问资源。这种机制的优点在于提高并发性能,特别是在读多写少的场景下。与互斥锁不同,读写锁允许并发读操作,从而提升了系统的吞吐量。

读写锁的工作原理
读锁(共享锁):多个线程可以同时持有读锁,从而允许多个线程同时读取数据。只要没有线程在写入,读操作可以并发执行。
写锁(独占锁):写操作需要独占锁。只有在没有任何线程持有读锁或写锁时,写锁才能被获取。写锁排斥其他读写操作,直到写入完成并释放锁。

初始化一个读写锁 pthread_rwlock_init
读锁定读写锁 pthread_rwlock_rdlock
非阻塞读锁定  pthread_rwlock_tryrdlock
写锁定读写锁 pthread_rwlock_wrlock
非阻塞写锁定 pthread_rwlock_trywrlock
解锁读写锁 pthread_rwlock_unlock
释放读写锁 pthread_rwlock_destroy

死锁

在这里插入图片描述
各自持有对方的锁导致的死锁问题。

条件变量

条件变量(Condition Variable)是一种线程同步机制,通常与互斥锁一起使用,用于在多线程环境下实现线程之间的等待和通知机制。条件变量可以让线程在等待某个特定条件时挂起,并在条件满足后被唤醒,从而避免不必要的忙等待,提高线程运行效率。

条件变量的工作原理
等待条件:线程调用 wait 等待某个条件,进入阻塞状态。此时条件变量会与一个互斥锁配合使用,以确保在等待条件时不会发生资源竞争。
通知条件:当条件满足时,另一个线程可以调用 signal 或 broadcast 来通知等待的线程继续运行。
signal:唤醒一个等待中的线程。
broadcast:唤醒所有等待中的线程。
使用场景
条件变量适用于需要等待某一特定条件的多线程场景。例如:

生产者-消费者模型:当生产者线程添加数据时,通过条件变量通知消费者线程开始消费。
资源池:线程在资源不足时等待,其他线程在资源充足时通知它们。

pthread_cond_wait(&m_cond,&m_mutex); 
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

注意事项
条件变量与互斥锁结合:条件变量的等待和通知操作通常需要和互斥锁一起使用,以防止出现竞态条件。
虚假唤醒:即使条件没有满足,线程也可能被唤醒,因此 pthread_cond_wait 必须在循环中使用以反复检查条件。

线程池的概念

线程池是一种多线程管理模式,用于限制并优化多线程的使用。它创建一组预先初始化的线程并将任务提交给这些线程,避免频繁的线程创建和销毁开销,从而提升系统性能和资源利用率。

在这里插入图片描述

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>#define POOL_NUM 4typedef struct Task
{void (*func)(int);int arg;struct Task *next;
} Task;typedef struct ThreadPool
{pthread_mutex_t taskLock;pthread_cond_t newTask;pthread_cond_t taskDone;pthread_t tid[POOL_NUM];Task *queue_head;Task *queue_tail;int busywork;int task_count;int shutdown; // 标志线程池是否关闭
} ThreadPool;ThreadPool *pool;void *threadFunction(void *arg)
{while (1){pthread_mutex_lock(&pool->taskLock);while (pool->queue_head == NULL && !pool->shutdown){pthread_cond_wait(&pool->newTask, &pool->taskLock);}if (pool->shutdown){pthread_mutex_unlock(&pool->taskLock);pthread_exit(NULL);}Task *task = pool->queue_head;pool->queue_head = task->next;if (pool->queue_head == NULL){pool->queue_tail = NULL;}pool->busywork++;pool->task_count--;pthread_mutex_unlock(&pool->taskLock);task->func(task->arg);// 目前任务还有多少个printf("task_count:%d\n", pool->task_count);free(task);pthread_mutex_lock(&pool->taskLock);pool->busywork--;if (pool->busywork == 0 && pool->task_count == 0){pthread_cond_signal(&pool->taskDone);}pthread_mutex_unlock(&pool->taskLock);}return NULL;
}void pool_add_task(void (*func)(int), int arg)
{Task *newTask = malloc(sizeof(Task));if (newTask == NULL){perror("Failed to allocate memory for new task");return;}newTask->func = func;newTask->arg = arg;newTask->next = NULL;pthread_mutex_lock(&pool->taskLock);if (pool->queue_tail == NULL){pool->queue_head = newTask;pool->queue_tail = newTask;}else{pool->queue_tail->next = newTask;pool->queue_tail = newTask;}pool->task_count++;pthread_cond_signal(&pool->newTask);pthread_mutex_unlock(&pool->taskLock);
}void initThreadPool()
{pool = malloc(sizeof(ThreadPool));pthread_mutex_init(&pool->taskLock, NULL);pthread_cond_init(&pool->newTask, NULL);pthread_cond_init(&pool->taskDone, NULL);pool->queue_head = NULL;pool->queue_tail = NULL;pool->busywork = 0;pool->task_count = 0;pool->shutdown = 0;for (int i = 0; i < POOL_NUM; i++){pthread_create(&pool->tid[i], NULL, threadFunction, NULL);}
}void destroyThreadPool()
{pthread_mutex_lock(&pool->taskLock);while (pool->task_count > 0 || pool->busywork > 0){pthread_cond_wait(&pool->taskDone, &pool->taskLock);}pool->shutdown = 1;pthread_cond_broadcast(&pool->newTask);pthread_mutex_unlock(&pool->taskLock);for (int i = 0; i < POOL_NUM; i++){pthread_join(pool->tid[i], NULL);}pthread_mutex_destroy(&pool->taskLock);pthread_cond_destroy(&pool->newTask);pthread_cond_destroy(&pool->taskDone);free(pool);
}void realwork(int arg)
{printf("Processing task with arg: %d\n", arg);sleep(1); // Simulate work
}int main()
{initThreadPool();for (int i = 0; i < 100; i++){pool_add_task(realwork, i);}destroyThreadPool();return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SSH实验5密钥登录Linuxroot用户(免密登录)

当用户尝试通过SSH连接到远程服务器时&#xff0c;客户端会生成一对密钥&#xff1a;公钥和私钥。公钥被发送到远程服务器&#xff0c;并存储在服务器的~/.ssh/authorized_keys文件中。而私钥则由客户端保管&#xff0c;不会传输给服务器。 在连接过程中&#xff0c;客户端使用…

域名邮箱推荐:安全与稳定的邮件域名邮箱!

域名邮箱推荐及绑定攻略&#xff1f;最好用的域名邮箱服务推荐&#xff1f; 域名邮箱&#xff0c;作为一种个性化且专业的电子邮件服务&#xff0c;越来越受到企业和个人的青睐。烽火将详细介绍域名邮箱登录的全过程&#xff0c;从注册到登录&#xff0c;帮助您轻松掌握这一重…

政治经济学笔记

【拯救者】政治经济学速成&#xff08;基础习题&#xff09; 研究生产关系必须联系生产力和上层建筑 1.生产力与生产关系 生产力代表生产的物质内容&#xff0c;生产关系是生产的社会形式。生产力决定生产关系&#xff0c;生产关系对生产力具有 反作用 *其中的”反作用”指的是…

《TCP/IP网络编程》学习笔记 | Chapter 7:优雅地断开套接字连接

《TCP/IP网络编程》学习笔记 | Chapter 7&#xff1a;优雅地断开套接字连接 《TCP/IP网络编程》学习笔记 | Chapter 7&#xff1a;优雅地断开套接字连接基于 TCP 的半关闭单方面断开连接带来的问题套接字和流针对优雅断开的 shutdown 函数为何需要半关闭&#xff1f;基于半关闭…

python | 包

1. 在python中什么是包&#xff1f; ​ 包是一种组织代码的方式&#xff0c;如下图所示红色部分目录mypackage就称为一个包&#xff0c;它之所以称为一个包完全是因为它里面有蓝色方框里的文件__init__.py。 ​ 这个目录被定义为一个包之后&#xff0c;我们就可以通过import来…

Qt信号和槽-->day04

Qt信号和槽 标准的信号和槽函数Qt中的槽函数Qt中的信号 connect案例 自定义信号和槽案例分析 信号槽的拓展信号连接信号案例 信号槽的两种连接方式Qt5中的处理方式Qt4中的处理方式Qt5处理信号槽重载问题案例 lambda表达式简单案例Qt中的应用 补充知识点 标准的信号和槽函数 QW…

Golang | Leetcode Golang题解之第552题学生出勤记录II

题目&#xff1a; 题解&#xff1a; const mod int 1e9 7type matrix [6][6]intfunc (a matrix) mul(b matrix) matrix {c : matrix{}for i, row : range a {for j : range b[0] {for k, v : range row {c[i][j] (c[i][j] v*b[k][j]) % mod}}}return c }func (a matrix) p…

腾讯首个3D生成大模型Hunyuan3D-1.0分享

Hunyuan3D-1.0是腾讯混元团队开发的首个同时支持文字、图像转3D的大模型&#xff0c;可以基于文本描述或单张图像生成3D模型。 Hunyuan3D-1.0采用了多视图生成和多视图重建两阶段的方法&#xff0c;能够从不同视角捕捉对象的纹理和几何信息。 在多视图生成阶段&#xff0c;Hu…

【JavaEE初阶 — 多线程】内存可见性问题 volatile

1. 内存可见性问题 内存可见性的概念 什么是内存可见性问题呢&#xff1f; 当一个线程对共享变量进行了修改&#xff0c;那么另外的线程都是立即可以看到修改后的最新值。在Java中&#xff0c;可以借助 synchronized、volatile 以及各种Lock 实现可见性。如果我们将变量声…

排序算法.

排序算法是最常用的一种算法.它解决的主要问题是在一定的时间复杂度和空间复杂度的条件下,对n个数按照一定的顺序进行排序.排序算法主要分为四大类,即插入类,交换类,选择类和归并类,不同的排序算法的时间复杂程度和空间复杂程度差别很大. 排序算法主要有以下几种: 1.插入类排…

iOS18.1通話錄音實測 錄音夠清晰 文字轉錄廣東話用唔到?

iOS 18.1功能實測&#xff5c;期待已久的通話錄音功能終在iOS18.1推出&#xff0c;讓用家可以在通話過程中輕鬆錄音&#xff0c;並附上逐字稿功能&#xff0c;為使用者提供更靈活的通話記錄方式。記者實測通話錄音功能&#xff0c;看看錄音清晰度、方便性、逐字轉錄的表現。 打…

403 Request Entity Too Lager(请求体太大啦)

昨天收到 QA 的生产报障&#xff0c;说是测试环境的附件上传功能报了 403 的错误&#xff0c;错误信息&#xff1a;403 Request Entity Too Lager。我尝试复现问题&#xff0c;发现传个几兆的文件都费劲啊&#xff0c;一传一个失败。不用说&#xff0c;项目用到 ng 代理&#x…

【C++】新手入门指南

> &#x1f343; 本系列为初阶C的内容&#xff0c;如果感兴趣&#xff0c;欢迎订阅&#x1f6a9; > &#x1f38a;个人主页:[小编的个人主页])小编的个人主页 > &#x1f380; &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 > ✌️ &#x1f91e; &#x1…

ElasticSearch备考 -- Cross cluster replication(CCR)

一、题目 操作在cluster1&#xff08;local&#xff09;中操作索引task&#xff0c;复制到cluster2&#xff08;remote&#xff09;中 二、思考 CCR 我们可以对标MySQL 理解为为主从&#xff0c;后者备份。主节点负责写入数据&#xff0c;从/备节点负责同步时主节点的数据。 …

IDEA在编译时: java: 找不到符号符号: 变量 log

一、问题 IDEA在编译的时候报Error:(30, 17) java: 找不到符号符号: 变量 log Error:(30, 17) java: 找不到符号 符号: 变量 log 位置: 类 com.mokerson.rabbitmq.config.RabbitMqConfig 二、解决方案 背景&#xff1a;下载其他同事代码时&#xff0c;第一次运行&#xff0c…

一文熟悉新版llama.cpp使用并本地部署LLAMA

0. 简介 最近是快到双十一了再给大家上点干货。去年我们写了一个大模型的系列&#xff0c;经过一年&#xff0c;大模型的发展已经日新月异。这一次我们来看一下使用llama.cpp这个项目&#xff0c;其主要解决的是推理过程中的性能问题。主要有两点优化&#xff1a; llama.cpp …

[翻译]ANSI X9.24-3-2017

目录 1 目的 2 范围 2.1 应用 3 参考文献 4 术语和定义 4.1 高级加密标准&#xff08;AES&#xff09; 4.2 AES 4.3 算法 4.4 ANSI 4.5 基础推导密钥(BDK) 4.6 BDK 4.7 BDK ID 4.8 加密密钥 4.9 加密密钥同步 4.10 密码强度 4.11 派生 4.12 派生标识符(ID) 4…

使用 GitHub Actions 部署到开发服务器的详细指南

使用 GitHub Actions 部署到开发服务器的详细指南 在本篇博客中&#xff0c;我们将介绍如何使用 GitHub Actions 实现自动化部署&#xff0c;将代码从 GitHub 仓库的 dev 分支自动部署到开发服务器。通过这种方式&#xff0c;可以确保每次在 dev 分支推送代码时&#xff0c;服…

Docker安装部署RabbitMQ

1. Docker环境准备 1.1 安装Docker 在开始Docker安装部署RabbitMQ之前&#xff0c;确保您的系统环境已经满足Docker的运行要求。以下是在不同操作系统上安装Docker的步骤和命令行演示。 对于Linux系统 在基于Debian的系统&#xff08;如Ubuntu&#xff09;上&#xff0c;您…

UniAPP u-popup 禁止背景滑动

增加class .NoScroll {overflow: hidden;position: fixed; }在外层div上增加该class判断条件