小白零基础--CPP多线程

进程

  • 进程就是运行中的程序
  • 线程=进程中的进程

1、C++11 Thread线程库基础

#include <iostream>
#include <thread>
#include<string>



void printthread(std::string msg){
  std::cout<<msg<<std::endl;
  for (int i = 0; i < 1000; i++)
  {
    std::cout<<"my  "<<i<<std::endl;
  }
  
  return;
}



int main(){
   //std::thread t(入口)
   //1、创建线程
    std::thread t(printthread,"hello thread");
   //2、保证等待线程结束,主线程在结束
   // t.join();
   //3、分离线程
   //t.detach();
   //4、joinable 判断是否可以调用join
   bool isjoin = t.joinable();
   if(isjoin){
      t.join();
   }
  std::cout<<"over"<<std::endl;
   system( "pause");
   
    return 0;
}

2、线程函数中的数据未定义错误

2.1 临时变量

错误例子

#include <iostream>
#include <thread>


void foo(int& x){
   x+=1;
}



int main(){
   //
   std::thread t(foo,1);
   t.join();
   system( "pause");
   
    return 0;
}

正确方案

#include <iostream>
#include <thread>


void foo(int& x){
   x+=1;
}



int main(){
   //
   int i=1;
   std::thread t(foo,std::ref(i));
   t.join();



  std::cout<<i<<std::endl;
   system( "pause");

    return 0;
}

2.2 传递指针/引用指向局部变量

2.1 的 i 在 main中,那要是不在呢?

#include <iostream>
#include <thread>

std::thread t;
void foo(int& x){
   x+=1;
}

void externi(){
  
    int i=1;
   t=std::thread (foo,std::ref(i));
}

int main(){
   //
   externi();
   t.join();



 
   system( "pause");

    return 0;
}

会报错
那怎么办呢?

#include <iostream>
#include <thread>

std::thread t;
 int i=1;
void foo(int& x){
   x+=1;
}

void externi(){
  
   
   t=std::thread (foo,std::ref(i));
}

int main(){
   //
   externi();
   t.join();



  std::cout<<i<<std::endl;
   system( "pause");

    return 0;
}

2.3 参数被提前手动释放

智能指针

#include <iostream>
#include <thread>
#include <memory>

class  myclass
{
private:
    /* data */
public:
    void foo(){
        std::cout<<"mememem"<<std::endl;
    }
};



int main(){
   std::shared_ptr<myclass> a=std::make_shared<myclass> ();
   std::thread t(&myclass::foo,a);

   system( "pause");
    return 0;
}

2.4 类的private函数

友元

#include <iostream>
#include <thread>
#include <memory>

class  myclass
{
private:
    friend void foo_thread();
     void foo(){
        std::cout<<"mememem"<<std::endl;
    }
public:
   
};
void foo_thread(){

    std::shared_ptr<myclass> a=std::make_shared<myclass> ();
    std::thread t(&myclass::foo,a);
    t.join();
}
int main(){
    foo_thread();
   system( "pause");
    return 0;
}

3互斥量

3.1数据共享–数据竞争问题

#include <iostream>
#include <thread>

int a = 0;
void func(){
    for (int i = 0; i < 1000000; i++)
    {
        a+=1;
    }
    
}
int main(){
   std::thread t1(func);
   std::thread t2(func);
   t1.join();
   t2.join();
  std::cout<<a<<std::endl;
   system( "pause");
    return 0;
}

3.2 互斥锁

#include <iostream>
#include <thread>
#include <mutex>
int a = 0;

std::mutex mt;
void func(){
    for (int i = 0; i < 1000000; i++)
    { 
        mt.lock();
        a+=1;
        mt.unlock();
    }
    
}
int main(){
   std::thread t1(func);
   std::thread t2(func);
   t1.join();
   t2.join();
  std::cout<<a<<std::endl;
   system( "pause");
    return 0;
}

3.3 理解线程安全

4互斥量死锁

4.1 死锁的概念

#include <iostream>
#include <thread>
#include <mutex>
using   namespace std;

mutex m1,m2;
void func1(){
    for (int i = 0; i < 100000; i++)
    { 
        m1.lock();
        m2.lock();
        m1.unlock();
        m2.unlock();
    }
    
    
}
void func2(){
       for (int i = 0; i < 100000; i++)
    { 
    m1.lock();
    m2.lock();
    m2.unlock();
    m1.unlock();
    }
}
int main(){
   thread t1(func1);
   thread t2(func2);
   t1.join();
   t2.join();
   
   cout<<"over<<"<<endl;
   system( "pause");
    return 0;
}

4.2 解决方案

5 lock_guard 和 std::unique_lock

5.1 lock_guard

#include <iostream>
#include <thread>
#include <mutex>
using   namespace std;
int a=0;
mutex m1;
void func1(){
    for(int i=0;i<10000;i++)
    {lock_guard<mutex> gm(m1);
      a++;
    
    }  
}

int main(){
   thread t1(func1);
   t1.join();
   cout<<a<<endl;
   system( "pause");
    return 0;
}

5.2 std::unique_lock

#include <iostream>
#include <thread>
#include <mutex>
using   namespace std;
int a=0;
mutex m1;
void func1(){
    for(int i=0;i<10000;i++)
    {
    unique_lock<mutex> gm(m1);
    a++;
    }  
}

int main(){
   thread t1(func1);
   t1.join();
   cout<<a<<endl;
   system( "pause");
    return 0;
}

6 call_once

6.1 单例模式

6.2 例子:日志类

#include <iostream>
#include <thread>
#include <mutex>
using   namespace std;
class Log{
public:
    Log(){};
    Log(const Log& log)=delete;
    Log &operator =(const Log& log)=delete;
    static Log& GetInstance(){
             static Log  log;//懒汉模式
             return log;
            //饿汉模式
            /**
            static Log   *log=nullptr;
            if(!log) log = new Log;
            return *log;
              */
    }
    void PrintLog(string msg){
            cout << __TIME__ <<" " <<msg<<endl;
    }
};

int main(){
   
   Log::GetInstance().PrintLog("error");

   system( "pause");
    return 0;
}

6.3 call_once

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

using namespace std;

// 需要一次性初始化的共享资源
class DatabaseConfig {
private:
    string serverAddress;
    int port;

    DatabaseConfig() : serverAddress("127.0.0.1"), port(3306) {
        cout << "数据库配置初始化完成!" << endl;
    }

public:
    static DatabaseConfig& getInstance() {
        static once_flag initFlag;
        static DatabaseConfig* instance = nullptr;

        call_once(initFlag, []() {
            instance = new DatabaseConfig();
        });

        return *instance;
    }

    void showConfig() {
        cout << "Server: " << serverAddress 
             << ":" << port << endl;
    }
};

// 多线程测试函数
void threadTask(int id) {
    this_thread::sleep_for(chrono::milliseconds(100 * id));
    auto& config = DatabaseConfig::getInstance();
    
    cout << "线程" << id << "获取配置:";
    config.showConfig();
}

int main() {
    vector<thread> threads;

    // 创建10个线程竞争访问
    for(int i = 0; i < 10; ++i) {
        threads.emplace_back(threadTask, i);
    }

    // 等待所有线程完成
    for(auto& t : threads) {
        t.join();
    }
   system("pause");
    return 0;
}
  • 合理使用 call_once 可以让多线程代码更简洁、更安全,尤其适合需要一次性初始化的场景

7 condition_variable

7.1 生产者-消费者模式概述

生产者-消费者模式是多线程编程中经典的同步问题,需要满足以下条件:

  1. 生产者线程生成数据并放入共享缓冲区。
  2. 消费者线程从缓冲区取出数据并处理。
  3. 同步要求
    • 缓冲区满时,生产者等待消费者消费数据。
    • 缓冲区空时,消费者等待生产者生产数据。

7.2 核心组件

  1. 共享缓冲区:通常使用队列(std::queue)实现。
  2. 互斥锁(std::mutex:保护对缓冲区的并发访问。
  3. 条件变量(std::condition_variable
    • not_full:生产者等待缓冲区非满。
    • not_empty:消费者等待缓冲区非空。

7.3实现代码

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>

using namespace std;

const int BUFFER_SIZE = 5;       // 缓冲区容量
queue<int> buffer;               // 共享缓冲区
mutex mtx;                       // 互斥锁
condition_variable not_full;     // 缓冲区非满条件
condition_variable not_empty;    // 缓冲区非空条件

// 生产者函数
void producer(int id) {
    for (int i = 0; i < 10; ++i) {
        unique_lock<mutex> lock(mtx);
        // 如果缓冲区满,等待消费者消费
        not_full.wait(lock, [] { 
            return buffer.size() < BUFFER_SIZE; 
        });
        // 生产数据
        int data = id * 100 + i;
        buffer.push(data);
        cout << "生产者 " << id << " 生产数据: " << data << endl;
        lock.unlock();
        not_empty.notify_one();  // 通知消费者
        this_thread::sleep_for(chrono::milliseconds(100));
    }
}

// 消费者函数
void consumer(int id) {
    for (int i = 0; i < 10; ++i) {
        unique_lock<mutex> lock(mtx);

        // 如果缓冲区空,等待生产者生产
        not_empty.wait(lock, [] { 
            return !buffer.empty(); 
        });

        // 消费数据
        int data = buffer.front();
        buffer.pop();
        cout << "消费者 " << id << " 消费数据: " << data << endl;

        lock.unlock();
        not_full.notify_one();   // 通知生产者

        this_thread::sleep_for(chrono::milliseconds(200));
    }
}

int main() {
    thread producers[2];
    thread consumers[3];

    // 启动2个生产者线程
    for (int i = 0; i < 2; ++i) {
        producers[i] = thread(producer, i);
    }

    // 启动3个消费者线程
    for (int i = 0; i < 3; ++i) {
        consumers[i] = thread(consumer, i);
    }

    // 等待所有线程结束
    for (auto& t : producers) t.join();
    for (auto& t : consumers) t.join();

    return 0;
}

7.4 代码解析

  1. 共享资源保护

    • 所有对缓冲区的操作(pushpop)均在互斥锁mtx的保护下进行。
    • 使用unique_lock自动管理锁的生命周期。
  2. 条件变量的使用

    • 生产者等待条件not_full.wait(lock, predicate)
      当缓冲区满时(buffer.size() >= BUFFER_SIZE),生产者线程阻塞,直到消费者消费数据后通过not_full.notify_one()唤醒。
    • 消费者等待条件not_empty.wait(lock, predicate)
      当缓冲区空时(buffer.empty()),消费者线程阻塞,直到生产者生产数据后通过not_empty.notify_one()唤醒。
  3. 通知机制

    • 生产者生产数据后调用not_empty.notify_one(),唤醒一个等待的消费者。
    • 消费者消费数据后调用not_full.notify_one(),唤醒一个等待的生产者。

7.5 运行结果示例

生产者 0 生产数据: 0
消费者 0 消费数据: 0
生产者 1 生产数据: 100
消费者 1 消费数据: 100
生产者 0 生产数据: 1
消费者 2 消费数据: 1
...
(输出将展示生产与消费的交替过程)

7.6 关键点总结

  1. 防止虚假唤醒
    条件变量的wait必须配合谓词(如buffer.size() < BUFFER_SIZE)使用,确保即使被意外唤醒也能重新检查条件。

  2. 资源管理

    • unique_lockwait时自动释放锁,唤醒后重新获取锁。
    • 使用notify_one而非notify_all,减少不必要的线程竞争。
  3. 死锁避免

    • 确保在调用notify_one前释放锁(通过lock.unlock())。
    • 避免在持有锁时进行耗时操作(如示例中的sleep_for在锁外执行)。

7.7 扩展场景

  • 多生产者和多消费者
    当前代码已支持多个生产者和消费者,通过调整线程数量即可验证。

  • 动态缓冲区大小
    可将BUFFER_SIZE设为动态值,根据需求调整。

  • 复杂数据类型
    queue<int>替换为自定义数据类型队列,实现更复杂的生产-消费逻辑。

此实现完整展示了如何利用condition_variable实现线程安全的生产者-消费者模式,可直接用于实际项目中的任务队列、线程池等场景。

8 跨平台线程池

#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>

class ThreadPool {
public:
    // 构造函数,启动指定数量的工作线程
    ThreadPool(size_t threads = std::thread::hardware_concurrency())
        : stop(false) {
        for(size_t i = 0; i < threads; ++i)
            workers.emplace_back([this] {
                for(;;) {
                    std::function<void()> task;

                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock,
                            [this]{ return this->stop || !this->tasks.empty(); });
                        
                        if(this->stop && this->tasks.empty())
                            return;
                        
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }

                    task();
                }
            });
    }

    // 将任务添加到任务队列,返回一个future以便获取结果
    template<class F, class... Args>
    auto 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();
        {
            std::unique_lock<std::mutex> lock(queue_mutex);

            // 不允许在停止线程池后添加新任务
            if(stop)
                throw std::runtime_error("enqueue on stopped ThreadPool");

            tasks.emplace([task](){ (*task)(); });
        }
        condition.notify_one();
        return res;
    }

    // 析构函数,等待所有任务完成并停止所有线程
    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for(std::thread &worker : workers)
            worker.join();
    }

private:
    std::vector<std::thread> workers;     // 工作线程集合
    std::queue<std::function<void()>> tasks; // 任务队列
    
    std::mutex queue_mutex;               // 任务队列互斥锁
    std::condition_variable condition;    // 条件变量
    bool stop;                            // 停止标志
};

// 使用示例
int main() {
    ThreadPool pool(4); // 创建4个工作线程

    // 提交多个任务到线程池
    std::vector<std::future<int>> results;

    for(int i = 0; i < 8; ++i) {
        results.emplace_back(
            pool.enqueue([i] {
                std::this_thread::sleep_for(std::chrono::seconds(1));
                return i*i;
            })
        );
    }

    // 获取任务结果
    for(auto && result : results)
        std::cout << result.get() << ' ';
    std::cout << std::endl;

    return 0;
}

9 异步并发 async future packaged task promise

#include <iostream>
#include <future>
#include <thread>
#include <chrono>
#include <vector>

int compute(int x) {
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
    return x * x;
}

int main() {
    // 使用 std::async 启动多个异步任务
    std::vector<std::future<int>> futures;
    for (int i = 1; i <= 5; ++i) {
        futures.push_back(std::async(std::launch::async, compute, i));
    }

    // 获取所有任务的结果
    for (auto& future : futures) {
        std::cout << "Result: " << future.get() << std::endl;
    }

    // 使用 std::packaged_task 手动控制任务执行
    std::packaged_task<int(int)> task(compute);
    std::future<int> future = task.get_future();

    std::thread t(std::move(task), 10);
    t.join(); // 等待线程完成

    std::cout << "Packaged Task Result: " << future.get() << std::endl;

    return 0;
}

10 原子操作atomic

#include <iostream>
#include <thread>
#include <atomic>
using namespace std;

atomic<int> a(0); // 使用 atomic<int> 替代普通 int

void func1() {
    for (int i = 0; i < 10000; i++) {
        a++; // 原子操作,无需额外的互斥锁
    }
}

int main() {
    thread t1(func1);
    t1.join();
    cout << a << endl; // 输出最终结果
    system("pause");
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

记录 | 基于MaxKB的文字生成视频

目录 前言一、安装SDK二、创建视频函数库三、调试更新时间 前言 参考文章&#xff1a;如何利用智谱全模态免费模型&#xff0c;生成大家都喜欢的图、文、视并茂的文章&#xff01; 自己的感想 本文记录了创建文字生成视频的函数库的过程。如果想复现本文&#xff0c;需要你逐一…

Redis|前言

文章目录 什么是 Redis&#xff1f;Redis 主流功能与应用 什么是 Redis&#xff1f; Redis&#xff0c;Remote Dictionary Server&#xff08;远程字典服务器&#xff09;。Redis 是完全开源的&#xff0c;使用 ANSIC 语言编写&#xff0c;遵守 BSD 协议&#xff0c;是一个高性…

安全防护前置

就业概述 网络安全工程师/安全运维工程师/安全工程师 安全架构师/安全专员/研究院&#xff08;数学要好&#xff09; 厂商工程师&#xff08;售前/售后&#xff09; 系统集成工程师&#xff08;所有计算机知识都要会一点&#xff09; 学习目标 前言 网络安全事件 蠕虫病毒--&…

开源2 + 1链动模式AI智能名片S2B2C商城小程序视角下从产品经营到会员经营的转型探究

摘要&#xff1a;本文聚焦于开源2 1链动模式AI智能名片S2B2C商城小程序&#xff0c;深入探讨在其应用场景下&#xff0c;企业从产品经营向会员经营转型的必要性与策略。通过分析如何借助该平台优化会员权益与价值&#xff0c;解决付费办卡的接受度问题&#xff0c;揭示其在提升…

让banner.txt可以自动读取项目版本

文章目录 1.sunrays-dependencies1.配置插件2.pluginManagement统一指定版本 2.common-log4j2-starter1.banner.txt使用$ 符号取出2.查看效果 1.sunrays-dependencies 1.配置插件 <!-- 为了让banner.txt自动获取版本号 --><plugin><groupId>org.apache.mave…

音视频多媒体编解码器基础-codec

如果要从事编解码多媒体的工作&#xff0c;需要准备哪些更为基础的内容&#xff0c;这里帮你总结完。 因为数据类型不同所以编解码算法不同&#xff0c;分为图像、视频和音频三大类&#xff1b;因为流程不同&#xff0c;可以分为编码和解码两部分&#xff1b;因为编码器实现不…

openmv运行时突然中断并且没断联只是跟复位了一样

就是 # 内存不足时硬件复位 except MemoryError as me: print("Memory Error:", me) pyb.hard_reset() # 内存不足时硬件复位 很有可能是你的代码加了内存溢出的复位&#xff0c;没加的话他会报错的

Redis集群理解以及Tendis的优化

主从模式 主从同步 同步过程&#xff1a; 全量同步&#xff08;第一次连接&#xff09;&#xff1a;RDB文件加缓冲区&#xff0c;主节点fork子进程&#xff0c;保存RDB&#xff0c;发送RDB到从节点磁盘&#xff0c;从节点清空数据&#xff0c;从节点加载RDB到内存增量同步&am…

77-《欧耧斗菜》

欧耧斗菜 欧耧斗菜&#xff08;学名&#xff1a;Aquilegia vulgaris L. &#xff09;是毛茛科耧斗菜属植物&#xff0c;株高30-60厘米。基生叶有长柄&#xff0c;基生叶及茎下部叶为二回三出复叶&#xff0c;小叶2-3裂&#xff0c;裂片边缘具圆齿。最上部茎生叶近无柄。聚伞花序…

为AI聊天工具添加一个知识系统 之83 详细设计之24 度量空间之1 因果关系和过程:认知金字塔

本文要点 度量空间 在本项目&#xff08;为AI聊天工具添加一个知识系统 &#xff09;中 是出于对“用”的考量 来考虑的。这包括&#xff1a; 相对-位置 力用&#xff08;“相”&#xff09;。正如 法力&#xff0c;相关-速度 体用 &#xff08;“体”&#xff09;。例如 重…

Unity 2D实战小游戏开发跳跳鸟 - 跳跳鸟碰撞障碍物逻辑

在有了之前创建的可移动障碍物之后,就可以开始进行跳跳鸟碰撞到障碍物后死亡的逻辑,死亡后会产生一个对应的效果。 跳跳鸟碰撞逻辑 创建Obstacle Tag 首先跳跳鸟在碰撞到障碍物时,我们需要判定碰撞到的是障碍物,可以给障碍物的Prefab预制体添加一个Tag为Obstacle,添加步…

记录 | Docker的windows版安装

目录 前言一、1.1 打开“启用或关闭Windows功能”1.2 安装“WSL”方式1&#xff1a;命令行下载方式2&#xff1a;离线包下载 二、Docker Desktop更新时间 前言 参考文章&#xff1a;Windows Subsystem for Linux——解决WSL更新速度慢的方案 参考视频&#xff1a;一个视频解决D…

[SAP ABAP] 在ABAP Debugger调试器中设置断点

在命令框输入/H&#xff0c;点击回车以后&#xff0c;调试被激活&#xff0c;点击触发任意事件进入ABAP Debugger调试器界面 点击按钮&#xff0c;可以在Debugger调试器中新增临时断点 我们可以从ABAP命令、方法、功能、表单、异常、消息、源代码等多个维度在Debugger调试器中设…

深度学习之“线性代数”

线性代数在深度学习中是解决多维数学对象计算问题的核心工具。这些数学对象包括标量、向量、矩阵和张量&#xff0c;借助它们可以高效地对数据进行操作和建模。以下将详细介绍这些数学对象及其在深度学习中的典型用途。 数学对象概述 标量 标量是最简单的数学对象&#xff0…

【面经】字节南京一面部分题目记录

南京字节一面题&#xff0c;可能因为项目不太匹配&#xff0c;全程八股比较多&#xff0c;也有两道手撕代码题&#xff0c;强度还是有的。为了方便大家学习&#xff0c;大部分答案由GPT整理&#xff0c;有些题给出了我认为回答比较好的博客链接。 文章目录 一、python2 和 pyth…

17.3.4 颜色矩阵

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 17.3.4.1 矩阵基本概念 矩阵&#xff08;Matrix&#xff09;是一个按照长方阵列排列的复数或实数集合&#xff0c;类似于数组。 由…

LabVIEW在电机自动化生产线中的实时数据采集与生产过程监控

在电机自动化生产线中&#xff0c;实时数据采集与生产过程监控是确保生产效率和产品质量的重要环节。LabVIEW作为一种强大的图形化编程平台&#xff0c;可以有效实现数据采集、实时监控和自动化控制。详细探讨如何利用LabVIEW实现这一目标&#xff0c;包括硬件选择、软件架构设…

mybatis(78/134)

前天学了很多&#xff0c;关于java的反射机制&#xff0c;其实跳过了new对象&#xff0c;然后底层生成了字节码&#xff0c;创建了对应的编码。手搓了一遍源码&#xff0c;还是比较复杂的。 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE …

【NLP251】Transformer精讲 残差链接与层归一化

精讲部分&#xff0c;主要是对Transformer的深度理解方便日后从底层逻辑进行创新&#xff0c;对于仅应用需求的小伙伴可以跳过这一部分&#xff0c;不影响正常学习。 1. 残差模块 何凯明在2015年提出的残差网络&#xff08;ResNet&#xff09;&#xff0c;Transformer在2016年…

全程Kali linux---CTFshow misc入门(25-37)

第二十五题&#xff1a; 提示&#xff1a;flag在图片下面。 直接检查CRC&#xff0c;检测到错误&#xff0c;就直接暴力破解。 暴力破解CRC的python代码。 import binascii import struct def brute_force_ihdr_crc(filename): # 读取文件二进制数据 with open(filen…