异构计算关键技术之多线程技术(四)

2024-03-14 18:44

本文主要是介绍异构计算关键技术之多线程技术(四),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

异构计算关键技术之多线程技术(四)

最近遇到了一个项目,需要写一个用户态的测试程序(独立进程),用来测试FPGA PCIe DMA的性能,具体的要求如下:

1. 需要一个主线程,用来配置FPGA的寄存器,同时启动从线程;2. 如果不进行人为干涉,子线程一直进行FPGA的相关操作,比如下发trigger信号、配置burst次数、数据长度;3. 进行人为干涉,子线程退出,并返回子线程执行的一些信息,提供给主线程做统计和计算信息;

下面我们直接给出相关的核心代码,结合线程的理论进行分析:

...
...static int run = 0;
static int round = 0;...typedef struct _param
{struct util_mem *util;int burst;int len;
}param;void *
recv_perf(void *data)
{...cpu_set_t mask;CPU_ZERO(&mask);CPU_SET(14, &mask);sched_setaffinity(0, sizeof(cpu_set_t), &mask);.../* len and burst*/reg_write(..., addr, (p->len&0x0000ffff)|((p->burst&0x0000ffff)<<16)));...while(run) {/* trigger */reg_write(..., addr, &rdata);while(times < p->burst) {data_size = recv(...,...,...);if (data_size == xxx) {...times++;}}times = 0;cnt++;}round = cnt * p->burst;pthread_exit(0);
}int main(int argc, char **argv)
{...pthread_t tid;ret = start(...);ret = pthread_create(&tid, NULL, recv_perf, &data);if (ret < 0) {}else {}pthread_detach(tid);while(1) {ch = getchar();if (ch = 's') {run = 0;...break;}}...avg = (float)recv_total/round;...
}

一、代码设计分析

这段代码非常实用,整体思想如下:

  • 主线程main函数,首先做了FPGA系统的一些初始化功能,然后起了一个从线程recv_perf();

  • 从线程主要是根据传递的参数发送给FPGA,让FPGA一直做DMA操作;

  • 主从线程分离detach();

  • 主线程while(1)循环,用来控制从线程的结束,同时通过全局变量进行传递参数;

  • 最后计算FPGA的统计信息;


在这里插入图片描述

二、C++多线程编程知识点归纳

1. 主线程和子线程的区别

我们先看看线程是如何创建起来的:

进程仅仅是一个容器,包含了线程运行中所需要的数据结构等信息。一个进程创建时,操作系统会创建一个线程,这就是主线程。而其他的从线程,却要主线程的代码来创建,也就是由程序员来创建。

主线程

main()函数均视为主线程,除了“不包含在thread里面的程序”,均视为主线程;

子线程

包含在thread = new thread()里面均视为子线程;

main函数

main()函数作为入口开始运行,是一个进程,同时也是一个线程。在现在的操作系统中,都是多线程的。

2. 线程的创建与参数传递

这个实例中,我们需要做一个子线程,用来一直执行FPGA的操作,同时我们需要传递FPGA的配置参数,下发给FPGA寄存器空间。

linux下的多线程程序,需要使用pthread.h,链接时需要使用libthread.a。

线程的创建需要通过pthread_create来完成,声明如下:

#include <pthread.h>int pthread_create(pthread_t *thread, pthread_attr_t *attr, void* (start_routine)(void*), void *arg);
  • thread:是一个指针,线程创建成功时,用以返回创建的线程ID;
  • attr:线程属性,NULL表示使用默认;
  • start_rountine:函数指针,指被创建的线程函数;
  • arg:该参数指向传递给线程函数的参数;

实例中,接收函数recv_perf(),同时传递的参数结构体data;

3. 线程的退出

多线程中,终止执行的方式有3种,分别是:

1. 线程执行完成后,自行终止;
2. 线程执行种,遇到了pthread_exit()或者return;
3. 线程在执行过程种,接收到了其他线程发送的“终止执行”的信息,然后终止执行;

第一种很容易理解,不做讨论。

pthread_exit()和return
:

return

return 关键字用于终止函数执行,必要时还能将函数的执行结果反馈给调用者。
return 关键字不仅可以用于普通函数,线程函数中也可以使用它。

pthread_exit()

<pthread.h>头文件中,提供有一个和 return 关键字相同功能的 pthread_exit() 函数。
和之前不同,pthread_exit() 函数只适用于线程函数,而不能用于普通函数。
void pthread_exit(void*retval);

retval是void*类型的指针,可以指向任何类型的数据,它指向的数据作为线程退出的返回值。

pthread_exit()和return()的区别

  • return:不仅会终止主线程执行,还会终止其他子线程的执行;
  • pthread_exit():只会终止当前线程,不会影响到其他线程的执行;

实际场景中,想要终止某个子线程,强烈建议使用pthread_exit()函数。

pthread_cancel
:

一个线程还可以向另一个线程发送“终止执行”的信号(后续称为“cancel”信号),这时候需要调用pthread_cancel()函数。


int pthread_cancel(pthread_t thread);

参数thread用于接收cancel信号的目标线程。

对于接收cancel信号后,结束执行的目标线程,等同于该线程自己执行如下语句:

pthread_exit(PTHREAD_CANCELED);

也就是说,当一个线程被强制终止时,它会返回pthread_cancel这个宏。

然后对于我们这个设计,巧妙的使用了run这个全局变量,用来控制子线程执行,同时利用全局变量来进行计算,是个很好的策略。

这是因为子线程在detach()以后,就无法再返回子线程的资源,会出现core。

4. detach()

detach()的作用是将子线程和主线程的关联分离,也就是说detach()后子线程在后台独立继续执行,主线程无法再获得子线程的控制权。

即使主线程结束,子线程未执行也不会结束。当主线程结束时,由运行时库负责清理和子线程相关的资源。

detach()同时也带来了一些问题,如子线程要访问主线程的对象,而主线中的对象又因为主线程结束而被销毁,导致程序崩溃。

5. 把进程/线程绑定到特定的cpu核上运行

某个进程需要较高的运行效率时,就有必要考虑将其绑定到单独的核上运行,以减小由于在不同的核上调度造成的开销。

把某个进程/线程绑定到特定的cpu核上后,该进程就会一直在此核上运行,不会再被操作系统调度到其他核上。但绑定的这个核还是可能会被调度运行其他应用程序的。(可以做隔离)

查看绑定情况

taskset -p pid

显示的是十进制,需要转换成2进制,每个1对应一个cpu(cpu从0开始)

启动时绑定

taskset -c xxx,yyy ./pcie_perf&

启动应用程序的时候绑定。

启动后绑定

taskset -cp 1,2,5,11 9865  将进程9864绑定到#1、#2、#5、#11号核上面。taskset -cp 1,2,5-11 9865  将进程9864绑定到#1、#2、#5~#11号核上面。

代码绑定

...
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(14, &mask);
sched_setaffinity(0, sizeof(cpu_set_t), &mask);
...

三、未完待续

欢迎关注知乎:北京不北,+vbeijing_bubei欢迎+V:beijing_bubei欢迎关注douyin:near.X (北京不北)获得免费答疑,长期技术交流。

四、参考文献

https://blog.csdn.net/qq_41854911/article/details/118718824

这篇关于异构计算关键技术之多线程技术(四)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/809359

相关文章

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

Python多线程应用中的卡死问题优化方案指南

《Python多线程应用中的卡死问题优化方案指南》在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助... 目录问题描述优化方案1. 网络请求优化2. 多线程架构优化3. 全局异常处理4. 配置管理优化优化效果1.

Python中高级文本模式匹配与查找技术指南

《Python中高级文本模式匹配与查找技术指南》文本处理是编程世界的永恒主题,而模式匹配则是文本处理的基石,本文将深度剖析PythonCookbook中的核心匹配技术,并结合实际工程案例展示其应用,希... 目录引言一、基础工具:字符串方法与序列匹配二、正则表达式:模式匹配的瑞士军刀2.1 re模块核心AP

Qt中实现多线程导出数据功能的四种方式小结

《Qt中实现多线程导出数据功能的四种方式小结》在以往的项目开发中,在很多地方用到了多线程,本文将记录下在Qt开发中用到的多线程技术实现方法,以导出指定范围的数字到txt文件为例,展示多线程不同的实现方... 目录前言导出文件的示例工具类QThreadQObject的moveToThread方法实现多线程QC

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (

RabbitMQ消费端单线程与多线程案例讲解

《RabbitMQ消费端单线程与多线程案例讲解》文章解析RabbitMQ消费端单线程与多线程处理机制,说明concurrency控制消费者数量,max-concurrency控制最大线程数,prefe... 目录 一、基础概念详细解释:举个例子:✅ 单消费者 + 单线程消费❌ 单消费者 + 多线程消费❌ 多

Python实现PDF按页分割的技术指南

《Python实现PDF按页分割的技术指南》PDF文件处理是日常工作中的常见需求,特别是当我们需要将大型PDF文档拆分为多个部分时,下面我们就来看看如何使用Python创建一个灵活的PDF分割工具吧... 目录需求分析技术方案工具选择安装依赖完整代码实现使用说明基本用法示例命令输出示例技术亮点实际应用场景扩

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Qt如何实现文本编辑器光标高亮技术

《Qt如何实现文本编辑器光标高亮技术》这篇文章主要为大家详细介绍了Qt如何实现文本编辑器光标高亮技术,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录实现代码函数作用概述代码详解 + 注释使用 QTextEdit 的高亮技术(重点)总结用到的关键技术点应用场景举例示例优化建议