c++多线程

🌕多线程

🌙线程的创建

std::thread 线程名(执行函数,要传递的变量1...)

🌙启动线程

线程名.join();

当你在一个线程上调用 join 方法时,当前线程将等待这个线程执行完毕,然后再继续执行后续的代码。

🌙使用互斥锁对变量进行锁定

std::mutex mtx; // 互斥锁,用于保护共享数据

int max_num = 10;

# 在执行函数中加入: 
std::lock_guard<std::mutex> lock(mtx); // 自动加锁和解锁

🌙输出奇数偶数案例

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>

std::mutex mtx; // 互斥锁,用于保护共享数据

void print_even(int max_num) {
    for (int i = 0; i <= max_num; i += 2) {
        std::lock_guard<std::mutex> lock(mtx); // 自动加锁和解锁
        std::cout << "Even: " << i << std::endl;
    }
}

void print_odd(int max_num) {
    for (int i = 1; i <= max_num; i += 2) {
        std::lock_guard<std::mutex> lock(mtx); // 自动加锁和解锁
        std::cout << "Odd: " << i << std::endl;
    }
}

int main() {
    int max_num = 10;

    // 创建两个线程,分别执行 print_even 和 print_odd 函数
    std::thread even_thread(print_even, max_num);
    std::thread odd_thread(print_odd, max_num);

    // 等待两个线程完成
    even_thread.join();
    odd_thread.join();

    std::cout << "Threads completed!" << std::endl;

    return 0;
}

🌕线程池

预先创建一组线程来处理任务,从而避免频繁创建和销毁线程的开销。

线程池中的线程可以重复使用,当有新的任务需要执行时,线程池会选择一个空闲线程来处理该任务,而不需要重新创建一个新的线程。这种方式可以提高性能和资源利用率。

🌙线程池模板

⭐include/thread_pool.h

#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <vector>               // 用于存储线程的向量
#include <queue>                // 用于存储任务的队列
#include <thread>               // 用于创建和管理线程
#include <mutex>                // 用于线程间同步
#include <condition_variable>   // 用于线程间的条件变量
#include <functional>           // 用于存储任务函数
#include <future>               // 用于处理异步任务结果

class thread_pool {
public:
    thread_pool(size_t); // 构造函数,初始化线程池
    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>; // 向线程池中添加任务
    ~thread_pool(); // 析构函数,销毁线程池

private:
    std::vector<std::thread> workers; // 工作线程
    std::queue<std::function<void()>> tasks; // 任务队列

    std::mutex queue_mutex; // 任务队列的互斥锁
    std::condition_variable condition; // 任务队列的条件变量
    bool stop; // 停止标志
};

#include "thread_pool.tpp" // 包含模板实现文件

#endif // THREAD_POOL_H

⭐include/thread_pool.tpp

// thread_pool.tpp

template<class F, class... Args>
auto thread_pool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
    using return_type = typename std::result_of<F(Args...)>::type; // 获取任务的返回类型

    auto task = std::make_shared<std::packaged_task<return_type()>>( // 创建一个包装任务
        std::bind(std::forward<F>(f), std::forward<Args>(args)...) // 绑定任务函数及其参数
    );

    std::future<return_type> res = task->get_future(); // 获取任务的 future 对象
    {
        std::unique_lock<std::mutex> lock(queue_mutex); // 加锁任务队列

        if(stop) // 如果线程池已经停止,抛出异常
            throw std::runtime_error("enqueue on stopped thread_pool");

        tasks.emplace([task](){ (*task)(); }); // 将任务添加到任务队列中
    }
    condition.notify_one(); // 通知一个等待的线程
    return res; // 返回任务的 future 对象
}

⭐src/thread_pool.cc

#include "thread_pool.h" // 包含线程池头文件

thread_pool::thread_pool(size_t threads) // 构造函数,初始化线程池
    : stop(false) { // 初始化停止标志为 false
    for(size_t i = 0; i < threads; ++i) // 创建指定数量的工作线程
        workers.emplace_back( // 将线程放入 workers 向量中
            [this] { // 使用 lambda 表达式
                for(;;) { // 无限循环,等待任务
                    std::function<void()> task; // 定义任务函数
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex); // 加锁任务队列
                        this->condition.wait(lock, // 等待任务队列不为空或停止标志为 true
                            [this]{ return this->stop || !this->tasks.empty(); });
                        if(this->stop && this->tasks.empty()) // 如果停止标志为 true 且任务队列为空,退出循环
                            return;
                        task = std::move(this->tasks.front()); // 获取任务队列中的任务
                        this->tasks.pop(); // 移除任务队列中的任务
                    }
                    task(); // 执行任务
                }
            }
        );
}

thread_pool::~thread_pool() { // 析构函数,销毁线程池
    {
        std::unique_lock<std::mutex> lock(queue_mutex); // 加锁任务队列
        stop = true; // 设置停止标志为 true
    }
    condition.notify_all(); // 通知所有等待的线程
    for(std::thread &worker: workers) // 等待所有工作线程完成
        worker.join();
}

🌙使用线程池模板开启三个线程执行数字相加

实现total = number_a + number_b + number_c,而number_a的值为从1从加到10,number_b的值为从1加到50,number_c的值为从1加到100, 是不是用三个线程分别计算number_a,number_b,number_c比较好?

⭐main.cc

#include <iostream>      // 标准输入输出流
#include "thread_pool.h" // 包含线程池头文件

int calculate_sum(int start, int end)
{
    int sum = 0;
    for (int i = start; i <= end; ++i)
    {
        sum += i;
    }
    return sum;
}

int main()
{
    thread_pool pool(3); // 创建包含 3 个线程的线程池

    // 使用线程池计算 number_a, number_b 和 number_c
    auto future_a = pool.enqueue(calculate_sum, 1, 10);  // 计算 number_a 的值
    auto future_b = pool.enqueue(calculate_sum, 1, 50);  // 计算 number_b 的值
    auto future_c = pool.enqueue(calculate_sum, 1, 100); // 计算 number_c 的值

    // 获取计算结果
    int number_a = future_a.get();
    int number_b = future_b.get();
    int number_c = future_c.get();

    // 计算总和
    int total = number_a + number_b + number_c;

    // 打印结果
    std::cout << "number_a: " << number_a << std::endl;
    std::cout << "number_b: " << number_b << std::endl;
    std::cout << "number_c: " << number_c << std::endl;
    std::cout << "total: " << total << std::endl;

    return 0;
}

⭐cmakelists.txt

cmake_minimum_required(VERSION 3.10)

# 设置项目名称
project(multithreading_test)

# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 包含头文件目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

# 添加可执行文件
add_executable(multithreading_test main.cc src/thread_pool.cc)

# 链接线程库
find_package(Threads REQUIRED)
target_link_libraries(multithreading_test Threads::Threads)

set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/multithreading_test)
install(TARGETS multithreading_test DESTINATION ./) # 把可执行文件安装到CMAKE_INSTALL_PREFIX当前目录下

⭐build.sh

#!/bin/bash
# build.sh

# 使用 set -e 命令,如果任何命令返回非零状态,脚本将立即退出
set -e

# 设置编译器路径 
GCC_COMPILER=/usr

# 获取当前目录
ROOT_PWD=$( cd "$( dirname $0 )" && pwd )

# 设置build的位置
BUILD_DIR=${ROOT_PWD}/build

# 设置install的位置
INSTALL_DIR=${ROOT_PWD}/install


# 如果存在 install 目录,则删除它
if [[ -d "${INSTALL_DIR}" ]]; then
  echo "Install directory exists. rm -f install"
  rm -rf ${INSTALL_DIR}
fi

# 如果没有build目录则创建它
if [[ ! -d "${BUILD_DIR}" ]]; then
  mkdir -p ${BUILD_DIR}
fi

# 检查build目录是否为空
if [[ "$(ls -A ${BUILD_DIR})" ]]; then
  # 如果build目录不为空,则执行make clean
  echo "Build directory is not empty. make clean"
  make -C ${BUILD_DIR} clean
fi

# 检查是否存在CMakeCache.txt文件
if [[ -f "${BUILD_DIR}/CMakeCache.txt" ]]; then
  # 删除CMakeCache.txt文件
  echo "CMakeCache.txt found. rm CMakeCache.txt"
  rm -f "${BUILD_DIR}/CMakeCache.txt"
fi

cd ${BUILD_DIR}
cmake .. \
    -DCMAKE_C_COMPILER=${GCC_COMPILER}/bin/gcc \
    -DCMAKE_CXX_COMPILER=${GCC_COMPILER}/bin/g++
make -j$(nproc)
make install

# 回到当前目录
cd -

# # 修改install中lib_shared目录名为lib
# mv install/yolov5_rtsp_rv1126/lib_shared install/yolov5_rtsp_rv1126/lib

####################################################### adb_push操作 #####################################################

# 示例变量
VAR1=1
VAR2=0

#如果 1==1则执行adb相关命令
if [ "$VAR1" -eq "$VAR2" ]; then
    # 条件为真时执行的命令
    echo "1==1,exec follow adb push commond."

    # 获取输入参数
    PARAM1="1"
    PARAM2="1"

    # 检查输入参数数量
    if [ "$#" -e 2 ]; then
        # echo "Usage: $0 <is yolov5_rtsp_rv1126 exist> <is /tmp/lib exist>"
        # 获取输入参数
        PARAM1="$1"
        PARAM2="$2"
    fi

    # adb操作
    REMOTE_PATH="/tmp"
    REMOTE_LIB_PATH="${REMOTE_PATH}/lib"
    REMOTE_TARGET_PATH="${REMOTE_PATH}/yolov5_rtsp_rv1126"
    RV1126_LD_LIBRARY_PATH=/oem/usr/lib:/oem/lib:/oem/usr/lib:/oem/lib:

    # 如果第一个参数为1,则表明yolov5_rtsp_rv1126存在
    if [ "$PARAM1" -eq 1 ]; then
        echo "Found $REMOTE_TARGET_PATH. Deleting..."
        adb shell rm -r "$REMOTE_TARGET_PATH"
        echo "$REMOTE_TARGET_PATH has been deleted."
    else
        echo "$REMOTE_TARGET_PATH does not exist."
    fi

    # 把编译好的install推送到板端
    adb push install/yolov5_rtsp_rv1126/ ${REMOTE_PATH}
    # 打印操作
    echo "adb push install/yolov5_rtsp_rv1126/ ${REMOTE_PATH}"



    # 检查远程目录是否存在,如果存在则跳过,不存在则创建它,并对libeasymedia.so进行软链接
    if [ "$PARAM2" -eq 1 ]; then
        echo "Remote directory ${REMOTE_LIB_PATH} already exists."
    else
        echo "Remote directory $REMOTE_LIB_PATH does not exist. Creating..."
        adb shell mkdir -p $REMOTE_LIB_PATH
        echo "Remote directory $REMOTE_LIB_PATH created."

        # 推送共享库到远程目录
        adb push ${SHARED_LIB_DIR}/* ${REMOTE_LIB_PATH}

        # 打印操作
        echo "adb push ${SHARED_LIB_DIR}/* ${REMOTE_LIB_PATH}"

        # 设置/tmp/lib为动态链接库,必须用单引号,否则会使用本机的$LD_LIBRARY_PATH
        adb shell export LD_LIBRARY_PATH=/tmp/lib:${RV1126_LD_LIBRARY_PATH}

        # 打印操作
        echo "adb shell export LD_LIBRARY_PATH=/tmp/lib:$RV1126_LD_LIBRARY_PATH"


        # 设置libeasymedia.so.1.0.1的软链接为libeasymedia.so.1
        adb shell ln -sf ${REMOTE_LIB_PATH}/libeasymedia.so.1.0.1 ${REMOTE_LIB_PATH}/libeasymedia.so.1

        # 打印操作
        echo "adb shell ln -s ${REMOTE_LIB_PATH}/libeasymedia.so.1.0.1 ${REMOTE_LIB_PATH}/libeasymedia.so.1"
    fi

else
    # 条件为假时执行的命令
    echo "1==0, only exec cmake and make install."
fi



🌕其它

🌙获取硬件并发数

int main() {
    unsigned int n = std::thread::hardware_concurrency();
    std::cout << "Number of concurrent threads supported: " << n << std::endl;
    return 0;
}

🌙互斥锁

⭐什么是互斥锁?

互斥锁(Mutex,Mutual Exclusion)是一种同步机制,用于防止多个线程同时访问共享资源,从而避免数据竞争和保持数据一致性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

computer_vision_chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值