C语言多线程编程:并发控制与同步机制

1. 引言

在多核处理器日益普及的今天,多线程编程已经成为提高程序性能的重要手段。C语言作为一种高效、底层的编程语言,自然也支持多线程编程。本文将带你深入了解C语言多线程编程的奥秘,并分享一些实用的编程技巧。

0a737f0c977846b3ac1e2f6d4bf8313f.png

2. 线程基础

2.1 线程的概念

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

#include <pthread.h>
void *thread_func(void *arg) {printf("Hello, world!\n");return NULL;
}
int main() {pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);pthread_join(tid, NULL);return 0;
}

2.2 线程的创建与销毁

C语言中使用pthread_create函数创建线程,使用pthread_join函数等待线程结束。

pthread_create(&tid, NULL, thread_func, NULL);
pthread_join(tid, NULL);

2.3 线程的属性

线程拥有属性,如线程分离状态、线程栈大小等。可以通过pthread_attr_t结构体来设置和获取线程属性。

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, thread_func, NULL);
pthread_attr_destroy(&attr);

3. 线程同步

3.1 互斥锁

互斥锁(Mutex)是一种常用的同步机制,用于保护共享资源,防止多个线程同时访问。

pthread_mutex_t lock;
pthread_mutex_init(&lock, NULL);
pthread_mutex_lock(&lock);
// 临界区代码
pthread_mutex_unlock(&lock);
pthread_mutex_destroy(&lock);

3.2 条件变量

条件变量(Condition Variable)是一种线程同步机制,用于线程间的条件等待和通知。

pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
pthread_cond_wait(&cond, &lock);
// 通知其他线程
pthread_cond_signal(&cond);
pthread_cond_destroy(&cond);

3.3 读写锁

读写锁(Read-Write Lock)允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。

pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
pthread_rwlock_rdlock(&rwlock);
// 读操作
pthread_rwlock_unlock(&rwlock);
pthread_rwlock_wrlock(&rwlock);
// 写操作
pthread_rwlock_unlock(&rwlock);
pthread_rwlock_destroy(&rwlock);

4. 线程通信

4.1 全局变量

全局变量是线程间共享的数据,可以用于线程通信。

int global_var = 0;
void *thread_func(void *arg) {global_var++;printf("global_var: %d\n", global_var);return NULL;
}

4.2 消息队列

消息队列(Message Queue)是一种常用的线程间通信机制,可以用于传递消息和数据。

mqd_t mq = mq_open("/my_queue", O_CREAT | O_RDWR, 0644, NULL);
mq_send(mq, "Hello, world!", 13, 0);
char buffer[64];
mq_receive(mq, buffer, 64, NULL);
mq_close(mq);
mq_unlink("/my_queue");

5. 线程实战技巧

5.1 线程池的魔力

线程池(Thread Pool)是一种预先创建了一定数量的线程,它们在池中等待任务分配的机制。这种设计避免了频繁创建和销毁线程的开销,提高了程序的性能和响应速度,尤其在处理大量短生命周期的任务时效果显著。

#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <queue>#define MAX_THREADS 4// 线程池结构体
typedef struct {pthread_mutex_t lock;pthread_cond_t notify;std::queue<void *(*)(void *)> task_queue;pthread_t *threads;bool shutdown;int thread_count;
} ThreadPool;// 线程池任务执行函数
void *threadpool_thread(void *threadpool);// 创建线程池
ThreadPool *threadpool_create(int thread_count);// 销毁线程池
int threadpool_destroy(ThreadPool *pool);// 向线程池添加任务
int threadpool_add_task(ThreadPool *pool, void *(*function)(void *), void *argument);// 线程池线程函数
void *threadpool_thread(void *threadpool) {ThreadPool *pool = (ThreadPool *)threadpool;while (1) {pthread_mutex_lock(&pool->lock);// 等待任务到来或线程池关闭while (pool->task_queue.empty() && !pool->shutdown) {pthread_cond_wait(&pool->notify, &pool->lock);}// 检查线程池是否关闭if (pool->shutdown) {break;}// 获取任务并执行void *(*func)(void *) = pool->task_queue.front();pool->task_queue.pop();pthread_mutex_unlock(&pool->lock);func(NULL);}pthread_mutex_unlock(&pool->lock);return NULL;
}// 创建线程池
ThreadPool *threadpool_create(int thread_count) {ThreadPool *pool = new ThreadPool;pool->thread_count = thread_count;pool->shutdown = false;pool->task_queue = std::queue<void *(*)(void *)>();pthread_mutex_init(&pool->lock, NULL);pthread_cond_init(&pool->notify, NULL);pool->threads = new pthread_t[thread_count];for (int i = 0; i < thread_count; i++) {pthread_create(&pool->threads[i], NULL, threadpool_thread, pool);}return pool;
}// 销毁线程池
int threadpool_destroy(ThreadPool *pool) {if (pool->shutdown) {return -1;}pool->shutdown = true;// 唤醒所有等待的线程pthread_cond_broadcast(&pool->notify);for (int i = 0; i < pool->thread_count; i++) {pthread_join(pool->threads[i], NULL);}pthread_mutex_destroy(&pool->lock);pthread_cond_destroy(&pool->notify);delete[] pool->threads;delete pool;return 0;
}// 向线程池添加任务
int threadpool_add_task(ThreadPool *pool, void *(*function)(void *), void *argument) {pthread_mutex_lock(&pool->lock);pool->task_queue.push(function);pthread_cond_signal(&pool->notify);pthread_mutex_unlock(&pool->lock);return 0;
}// 示例任务函数
void *my_task(void *arg) {printf("Task is running\n");sleep(1);return NULL;
}// 主函数
int main() {ThreadPool *pool = threadpool_create(MAX_THREADS);for (int i = 0; i < 10; i++) {threadpool_add_task(pool, my_task, NULL);}sleep(5); // 等待任务完成threadpool_destroy(pool);return 0;
}

在这个示例中,我们创建了一个简单的线程池,它可以执行通过threadpool_add_task添加的任务。每个线程都会在任务队列中获取任务并执行,直到线程池被关闭。这种设计使得线程可以复用,减少了线程创建和销毁的开销,非常适合于处理大量短生命周期的并发任务。

5.2 线程局部存储

线程局部存储(Thread-Local Storage,TLS)是一种机制,允许每个线程拥有自己的变量副本,这样可以避免在多线程环境中对全局变量进行同步。

__thread int tls_var = 0;void *thread_func(void *arg) {tls_var++;printf("Thread %ld's tls_var: %d\n", pthread_self(), tls_var);return NULL;
}

5.3 线程安全函数

在设计多线程程序时,需要确保使用的函数是线程安全的。对于那些不是线程安全的函数,可以通过加锁或其他同步机制来保证线程安全。

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;int thread_safe_sum(int a, int b) {pthread_mutex_lock(&lock);int result = a + b;pthread_mutex_unlock(&lock);return result;
}

6. 并发性能优化

6.1 线程亲和性

线程亲和性(Thread Affinity)是指将线程绑定到特定的CPU核心上,以减少线程在核心之间的迁移,提高缓存利用率。

cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset); // 绑定到第一个核心pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);pthread_create(&tid, &attr, thread_func, NULL);
pthread_attr_destroy(&attr);

6.2 lock-free 编程

lock-free 编程是一种避免使用锁的并发编程技术,通常涉及到原子操作和内存顺序。

#include <stdatomic.h>atomic_int counter = 0;void *thread_func(void *arg) {for (int i = 0; i < 1000000; i++) {atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed);}return NULL;
}

7. 结语

C语言的多线程编程是一个复杂但强大的工具,它可以让你的程序充分发挥多核处理器的性能。通过理解和运用线程同步、通信和性能优化技巧,你可以构建出高效、响应迅速的多线程应用程序。记住,多线程编程是提升性能的利器,但也要小心处理同步和竞争条件,以确保程序的正确性和稳定性。

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

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

相关文章

解决VIvado编程中遇到的bug 5

解决VIvado编程中遇到的bug 5 语言 &#xff1a;Verilg HDL EDA工具&#xff1a; Vivado、quartus2 、modelsim 解决VIvado编程中遇到的bug 5一、引言二、问题、分析及解决方法1. vivado编译时报错&#xff08;1&#xff09;错误&#xff08;2&#xff09;分析&#xff08;3&am…

YOLOv8 多种任务网络结构详细解析 | 目标检测、实例分割、人体关键点检测、图像分类

前言 本文仅根据模型的预测过程&#xff0c;即从输入图像到输出结果&#xff08;图像预处理、模型推理、后处理&#xff09;&#xff0c;来展现不同任务下的网络结构&#xff0c;OBB 任务暂不包含。 Backbone 1. yolov8m 2. yolov8m-p2 3. yolov8m-p6 4. 细节 图中 CBS Con…

DALL·E 2详解:人工智能如何将您的想象力变为现实!

引言 DALLE 2是一个基于人工智能的图像生成模型&#xff0c;它通过理解自然语言描述来生成匹配这些描述的图像。这一模型的核心在于其创新的两阶段工作流程&#xff0c;首先是将文本描述转换为图像表示&#xff0c;然后是基于这个表示生成具体的图像。 下面详细介绍DALL-E2的功…

Java高级---Spring Boot---3快速入门

3 Spring Boot快速入门 3.1 创建第一个Spring Boot项目 使用IntelliJ IDEA和Spring Initializr创建项目 打开IntelliJ IDEA 并选择 “Start a new project”。在新建项目向导中&#xff0c;选择 “Spring Initializr”。填写项目基本信息&#xff1a; Group: 定义项目组&…

C#使用GDI对一个矩形进行任意角度旋转

C#对一个矩形进行旋转GDI绘图&#xff0c;可以指定任意角度进行旋转 我们可以认为一张图片Image&#xff0c;本质就是一个矩形Rectangle,旋转矩形也就是旋转图片 在画图密封类 System.Drawing.Graphics中&#xff0c; 矩形旋转的两个关键方法 //设置旋转的中心点 public v…

生成随机图片

package com.zhuguohui.app.lib.tools;/*** Created by zhuguohui* Date: 2024/6/1* Time: 13:39* Desc:获取随机图片*/ public class RandomImage {// static final String url "https://picsum.photos/%d/%d?random%d";static final String url "https://…

FPGA定点数FFT过后转换为浮点数与Matlab计算的FFT结果进行比对

目录 1.前言2.FPGA的testbench中如何读取数据文件3.FPGA的testbench中如何将输出数据存储在文件中4.Matlab去读取testbench存储的文件数据4.1纯数字不带编码4.2 带编码的数据&#xff0c;如定点数 微信公众号获取更多FPGA相关源码&#xff1a; 1.前言 前面一篇文章讲了&…

ESP32-C3模组上实现蓝牙BLE配网功能(2)

接前一篇文章&#xff1a;ESP32-C3模组上实现蓝牙BLE配网功能&#xff08;1&#xff09; 本文内容参考&#xff1a; 《ESP32-C3 物联网工程开发实战》 乐鑫科技 蓝牙的名字由来是怎样的&#xff1f;为什么不叫它“白牙”&#xff1f; 特此致谢&#xff01; 一、蓝牙知识基础…

[数据集][目标检测]吉他检测数据集VOC+YOLO格式66张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;66 标注数量(xml文件个数)&#xff1a;66 标注数量(txt文件个数)&#xff1a;66 标注类别数…

Three.js——tween动画、光线投射拾取、加载.obj/.mtl外部文件、使用相机控制器

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 ⚡开源项目&#xff1a; rich-vue3 &#xff08;基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL&#xff09; &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1…

YOLOv8 segment介绍

1.YOLOv8图像分割支持的数据格式&#xff1a; (1).用于训练YOLOv8分割模型的数据集标签格式如下: 1).每幅图像对应一个文本文件&#xff1a;数据集中的每幅图像都有一个与图像文件同名的对应文本文件&#xff0c;扩展名为".txt"; 2).文本文件中每个目标(object)占一行…

iReport的下载与安装

下载官网&#xff1a;Home - Jaspersoft Community 网盘下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Oy5opY4GxPZ_mllTlBZ-2w 提取码&#xff1a;75do 安装就是双击后一直点击下一步 安装好了之后要配置jdk环境 iReport 目前 并不支持 高版本jdk 只支持…

TQSDRPI开发板教程:UDP收发测试

项目资源分享 链接&#xff1a;https://pan.baidu.com/s/1gWNSA9czrGwUYJXdeuOwgQ 提取码&#xff1a;tfo0 LWIP自环教程&#xff1a;https://blog.csdn.net/mcupro/article/details/139350727?spm1001.2014.3001.5501 在lwip自环的基础上修改代码实现UDP的收发测试。新建一…

嫁接打印:经济与实用的完美结合

在制造领域&#xff0c;寻求经济且好用的技术方案至关重要。而在模具制造中&#xff0c;3D 打印随形水路在提升冷却效率和产品良率方面的卓越表现已得到广泛认同。如何更经济的应用3D打印技术&#xff0c;就不得不说嫁接打印了。 在嫁接打印的制造过程中&#xff0c;产品的一部…

音视频开发—H264 SPS 和 PPS 参数说明

文章目录 序列参数集 (SPS, Sequence Parameter Set)SPS的主要内容&#xff1a; 图像参数集 (PPS, Picture Parameter Set)PPS的主要内容&#xff1a; Slice Header 结构 在H.264视频编码标准中&#xff0c;SPS和PPS是关键的参数集&#xff0c;它们提供了解码所需的各种配置信息…

Pytorch创建张量

文章目录 1.torch.from_numpy()2. torch.zeros()3. torch.ones()4. torch.arange()5. torch.linspace()6. torch.logspace()7. torch.eye()8. torch.empty()9. torch.full()10. torch.complex()10. torch.rand()10. torch.randint()11. torch.randn12. torch.normal()13. torch…

[数据集][目标检测]剪刀石头布检测数据集VOC+YOLO格式1973张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1973 标注数量(xml文件个数)&#xff1a;1973 标注数量(txt文件个数)&#xff1a;1973 标注…

Docker 简介和安装

目录 Docker 是什么 跟普通虚拟机的对比 打包、分发、部署 Docker 部署的优势 Docker 通常用来做什么 重要概念&#xff1a;镜像、容器 安装 镜像加速源 Docker 是什么 Docker 是一个应用打包、分发、部署的工具 你也可以把它理解为一个轻量的虚拟机&#xff0c;它只虚…

SAP揭秘者- SAP PP模块日常常见运维问题之工单入库失败原因分析及快速处理

文章摘要&#xff1a; 无论您是负责SAP实施项目还是负责SAP运维项目&#xff0c;当用户发现有SAP PP模块的各种异常问题的时都需要作为SAP PP顾问的您快速地理解用户提交的问题&#xff0c;并快速地解决这些问题&#xff0c; 上篇文章跟大家聊了基本单位维护错了怎么修改的解决…

ARC学习(2)基本编程模型认识(二)

笔者继续来学习一下arc的编程模型的寄存器信息。 1、core寄存器深入 参数寄存器&#xff1a;r0-r7&#xff0c;8个参数&#xff0c;暂存器&#xff1a;r10-r15保存寄存器&#xff1a;r16-r25 调用函数需要保存的寄存器指针寄存器&#xff1a;gp&#xff08;全局指针&#xff09…