并发 多线程

news2024/10/7 4:34:43

目录

thread

thread 类总览

构造函数

= join joinable

​编辑

detach swap yield swap

成员函数的调用

namespace::this_thread

线程同步--锁

互斥锁mutex

递归锁recursive_mutex

定时锁

Lock 锁辅助类

lock_guard​编辑

unique_lock

std::lock 解决死锁问题

消息队列

condition_variable

condition_variable

生产者消费者模型

atomic

call_once

​chrono 时间库


thread

thread 类总览

构造函数

= join joinable

  • 谁调用了这个函数?调用了这个函数的线程对象,一定要等这个线程对象的方法(在构造时传入的方法)执行完毕后(或者理解为这个线程的活干完了!),这个join()函数才能得到返回。

  • 在什么线程环境下调用了这个函数?上面说了必须要等线程方法执行完毕后才能返回,那必然是阻塞调用线程的,也就是说如果一个线程对象在一个线程环境调用了这个函数,那么这个线程环境就会被阻塞,直到这个线程对象在构造时传入的方法执行完毕后,才能继续往下走,另外如果线程对象在调用join()函数之前,就已经做完了自己的事情(在构造时传入的方法执行完毕),那么这个函数不会阻塞线程环境,线程环境正常执行

detach swap yield swap

成员函数的调用

必须传入成员函数地址和调用的对象

namespace::this_thread

线程同步--锁

互斥锁mutex

递归锁recursive_mutex

递归锁出现的意义:假设存在这样一个场景,一个函数使用mutex 同时调用另外的一个函数里面有用到同一个mutex,则此时同一个互斥量被上了两次锁,导致死锁;而递归锁可以对互斥量拥有多层所有权,可以避免死锁;

同一个线程多次占用(递归占用次数有限,不能太多),可配合lock_guard使用,不通线程和互斥锁一致。

定时锁

Lock 锁辅助类

lock_guard

unique_lock

比lock_guard更灵活,主要用与条件变量一同使用

std::lock 解决死锁问题

 

消息队列

#include <list>
#include <thread>
#include <mutex>
#include<iostream>
​
class A
{
public:
    //把收到的消息(玩家命令)入到一个队列的线程
    void inMsgRecvQueue()
    {
        for (int i = 0;i < 100;++i)
        {
            cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
            my_mutex.lock();
            msgRecvQueue.push_back(i);
            my_mutex.unlock();
        }
    }
​
    bool outMsgLULProc(int& command)
    {
        // my_mutex.lock();
        std::lock_guard<std::mutex> sguard(my_mutex);
        if (!msgRecvQueue.empty())
        {
            command = msgRecvQueue.front();
            msgRecvQueue.pop_front();
            // my_mutex.unlock();
            return true;
        }
        // my_mutex.unlock();
        return false; 
    }
​
    //把数据从消息队列中取出的线程
    void outMsgRecvQueue()
    {
        int command = 0;
        for (int i = 0;i < 100;i++)
        {
            bool result = outMsgLULProc(command);
            if (result == true)
            {
                cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
                //可以考虑对命令(数据)进行处理
            }
            else
            {
                cout << "outMsgRecvQueue()执行,但是目前消息队列中为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }
​
private:
    std::list<int> msgRecvQueue;    //容器,专门用于代表玩家发送过来的命令
    std::mutex my_mutex; //创建一个互斥量 
};
​
int main()
{
    A myobja;
    std::thread myOutnMsgObj(&A::outMsgRecvQueue,&myobja);
    std::thread myInMsgObj(&A::inMsgRecvQueue,&myobja);
    myInMsgObj.join();
    myOutnMsgObj.join();
    return 0;
}

condition_variable

condition_variable

生产者消费者模型

//condition_variable.h头文件
    
#include <mutex>
#include <thread>
#include <chrono>
#include <deque>
#include <condition_variable>
​
namespace TestConditional_variable
{
    extern std::mutex g_cvMutex;
    extern std::condition_variable g_cv;
    extern std::deque<int>g_data_deque;
    extern const int MAX_NUM;
    extern int g_next_index;
​
    const int PRODUCER_THREAD_NUM = 3;
    const int CONSUMER_THREAD_NUM = 3;
​
    void producer_thread(int thread_id);
    void consumer_thread(int thread_id);
​
}



//condition_variable.cpp头文件
    
#include"Condition_variable.h"
#include<iostream>
namespace TestConditional_variable {
​
     std::mutex g_cvMutex;
     std::condition_variable g_cv;
     std::deque<int>g_data_deque;
     const int MAX_NUM = 30;
​
     int g_next_index = 0;
    
    void producer_thread(int thread_id)
    {
        for (int i = 0; i < 4;i++)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            std::unique_lock<std::mutex>lk(g_cvMutex);
            g_cv.wait(lk, [](){return g_data_deque.size() <= MAX_NUM; });
            //wait的第二个参数为可执行的OBJ 反复执行直到返回true
            g_next_index++;
            g_data_deque.push_back(g_next_index);
            std::cout << "producer_thread" << thread_id << " producer data" << g_next_index;
            std::cout << " queue size:" << g_data_deque.size() << std::endl;
            g_cv.notify_all();
        }
    }
​
    void consumer_thread(int thread_id)
    {
        for (int i = 0; i < 4; i++)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            std::unique_lock<std::mutex>lk(g_cvMutex);
            g_cv.wait(lk, [](){return !g_data_deque.empty(); });
            g_next_index++;
            int data = g_data_deque.front();
            g_data_deque.pop_front();
            std::cout << "consumer_thread" << thread_id << " consumer data" << g_next_index;
            std::cout << " queue size:" << g_data_deque.size() << std::endl;
            g_cv.notify_all();
        }
    }
​
}




#include"Condition_variable.h"
#include<iostream>
​
int main(int argc, char *argv[])
{
    std::thread arrProducerThread[TestConditional_variable::PRODUCER_THREAD_NUM];
    std::thread arrConsumerThread[TestConditional_variable::CONSUMER_THREAD_NUM];
​
    for (int i = 0; i < TestConditional_variable::PRODUCER_THREAD_NUM; i++)
    {
        arrProducerThread[i] = std::thread(TestConditional_variable::producer_thread, i);
    }
    for (int i = 0; i < TestConditional_variable::CONSUMER_THREAD_NUM; i++)
    {
        arrConsumerThread[i] = std::thread(TestConditional_variable::consumer_thread, i);
    }
    for (int i = 0; i < TestConditional_variable::PRODUCER_THREAD_NUM; i++)
    {
        arrProducerThread[i].join();
    }
    for (int i = 0; i < TestConditional_variable::CONSUMER_THREAD_NUM; i++)
    {
        arrConsumerThread[i].join();
    }
    return 0;
}

atomic

原子变量

call_once

        在某些特定情况下,某些函数只能在多线程环境下调用一次,比如:要初始化某个对象而这个对象只能被初始化一次,就可以使用 std::call_once() 来保证函数在多线程环境下只能被调用一次。使用 call_once() 的时候,需要一个 once_flag 作为 call_once() 的传入参数。

           void call_once( std::once_flag& flag, Callable&& f, Args&&... args );

          flag:once_flag 类型的对象,要保证这个对象能够被多个线程同时访问到。

          f:回调函数,可以传递一个有名函数地址,也可以指定一个匿名函数

         args:作为实参传递给回调函数。

   

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

once_flag g_flag;
void do_once(int a, string b)
{
    cout << "name: " << b << ", age: " << a << endl;
}

void do_something(int age, string name)
{
    static int num = 1;
    call_once(g_flag, do_once, 19, "luffy");
    cout << "do_something() function num = " << num++ << endl;
}

void test_call_once()
{
    thread t1(do_something, 20, "ace");
    thread t2(do_something, 20, "sabo");
    thread t3(do_something, 19, "luffy");
    t1.join();
    t2.join();
    t3.join();
}

chrono 时间库

duration

定义于头文件 <chrono>
template<
    class Rep,
    class Period = std::ratio<1>
> class duration;

这是一个数值类型,表示时钟数(周期)的类型(默认为整形)。若 Rep 是浮点数,则 duration 能使用小数描述时钟周期的数目
Period:表示时钟的周期,它的原型如下
// 定义于头文件 <ratio>
template<
    std::intmax_t Num,//周期的分子
    std::intmax_t Denom = 1//周期的分母 默认为1
> class ratio;

ratio 类表示每个时钟周期的秒数,其中第一个模板参数 Num代表分子,Denom代表分母,该分母值默认为 1,因此,ratio代表的是一个分子除以分母的数值,比如:ratio<2> 代表一个时钟周期是 2 秒,ratio<60 > 代表一分钟,ratio<60*60 > 代表一个小时,ratio<60*60*24 > 代表一天。而 ratio<1,1000 > 代表的是 1/1000 秒,也就是 1 毫秒,ratio<1,1000000 > 代表一微秒,ratio<1,1000000000 > 代表一纳秒。

void test_chrono()
{
    using namespace std;
    chrono::hours h(1);                          // 一小时
    chrono::milliseconds ms{ 3 };                // 3 毫秒  初始化操作 ms{3} 表示一共有 3 个时间周期,每个周期为 1 毫秒
    std::chrono::microseconds us = 2 * ms;     // 6000 微秒
    chrono::duration<int, ratio<1000>> ks(3);    // 3000 秒

    // chrono::duration<int, ratio<1000>> d3(3.5);  // error
    //dd(6.6) 时钟周期为默认的 1 秒,共有 6.6 个时钟周期,所以 dd 表示的时间间隔为 6.6 秒
    chrono::duration<double> dd(6.6);               // 6.6 秒 周期类型为小数

    // 使用小数表示时钟周期的次数
    //hz(3.5) 时钟周期为 1 / 30 秒,共有 3.5 个时钟周期,所以 hz 表示的时间间隔为 1 / 30 * 3.5 秒
    chrono::duration<double, std::ratio<1, 30>> hz(3.5);
    //count统计周期数
    std::cout << "3 ms duration has " << ms.count() << " ticks\n"  //3
        << "6000 us duration has " << us.count() << " ticks\n"     //6000
        << "3.5 hz duration has " << hz.count() << " ticks\n";     //3.5

    //重载了++ -- + - = 等操作
    chrono::minutes t1(2);
    chrono::seconds t2(2);
    chrono::seconds t3 = t1 - t2;
    chrono::seconds t4 = t1 + t2;
    t4++;
    cout << t3.count() <<"seconds" << endl;//118seconds
    cout << t4.count() << "seconds" << endl;//123seconds
    /*
    注意事项:duration 的加减运算有一定的规则,当两个 duration 时钟周期不相同的时候,会先统一成一种时钟,然后再进行算术运算,统一的规则如下:假设有 ratio<x1,y1> 和 ratio<x2,y2 > 两个时钟周期,首先需要求出 x1,x2 的最大公约数 X,然后求出 y1,y2 的最小公倍数 Y,统一之后的时钟周期 ratio 为 ratio<X,Y>
    */
    
    chrono::duration<double, ratio<9, 7> >t5(3);//3*   9/7秒
    chrono::duration<double, ratio<4, 3>> t6(3); // 3 *   4/3秒
    chrono::duration<double, ratio<1, 21>> t7 = t6 - t5;
    cout << t7.count() << "ticks" << endl;//3ticks

}

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

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

相关文章

华为认证hcna题库背诵技巧有哪些?hcna和hcia有什么区别?

大家都知道华为认证hcna是有题库供考生刷题备考的&#xff0c;但题库中海量的题目想要在短时间背诵下来可并不是一件容易的事情&#xff0c;这就需要我们掌握一定的技巧才行。华为认证hcna题库背诵技巧有哪些? hcna和hcna这二者又有什么区别呢?今天的文章将为大家进行详细解…

鸿蒙开发设备管理:【@ohos.batteryInfo (电量信息)】

电量信息 该模块主要提供电池状态和充放电状态的查询接口。 说明&#xff1a; 本模块首批接口从API version 6开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import batteryInfo from ohos.batteryInfo;属性 描述电池信息。 系统能…

汇川学习笔记7 - 雕刻机项目

1、系统上电轴准备好之后&#xff0c;自动复回原点一次&#xff0c; 2、在雕刻机面板上有三个按钮用来控制画三种图形 3、注意cnc代码放置的文件夹 4、FILE0文件内容 5、FILE1文件内容 6、FILE2文件内容 7、程序代码下载地址 https://download.csdn.net/download/qq_6191667…

设置日历程序

目录 一 设计原型 二 后台源码 一 设计原型 二 后台源码 namespace 设置日历 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void dateTimePicker1_ValueChanged(object sender, EventArgs e){richTextBox1.Text dateTimePicker1.T…

CCSP自考攻略+经验总结

备考攻略 备考攻略准备阶段通读阶段精度阶段总复习阶段刷题阶段命运审判 写到最后 备考攻略 趁着对ssp知识点的理解还在&#xff0c;开始ccsp的考证之路&#xff0c;文章结构还是按照cissp备考篇的结构梳理。本次备考和cissp的离职在家备考不同&#xff0c;ccsp是在职利用非工…

鸿蒙开发Ability Kit(程序框架服务):【ServiceAbility切换】 组件切换

ServiceAbility切换 FA模型中的ServiceAbility对应Stage模型中的ServiceExtensionAbility。Stage模型下的ServiceExtensionAbility为系统API&#xff0c;只有系统应用才可以创建。因此&#xff0c;FA模型的ServiceAbility的切换&#xff0c;对于系统应用和三方应用策略有所不同…

【深度学习】实践方法论

李宏毅深度学习笔记 优化问题 训练数据的损失不够低的时候&#xff0c;到底是模型偏差&#xff0c;还是优化的问题&#xff1f; 判断方法是通过比较不同的模型来判断模型现在到底够不够大 看到一个从来没有做过的问题&#xff0c;可以先跑一些比较小的、比较浅的网络&#x…

React@16.x(42)路由v5.x(7)常见应用场景(4)- 路由切换动画

目录 1&#xff0c;实现路由切换基础样式 2&#xff0c;使用 CSSTransition 添加动画1&#xff0c;自定义动画组件 *TransitionRoute.jsx*2&#xff0c;*App.jsx*3&#xff0c;样式改动 3&#xff0c;注意点 通过一个例子来说明如何实现。 1&#xff0c;实现路由切换 基础样式…

【Linux:文件描述符】

文件描述符&#xff1a; 文件描述符的分配原则&#xff1a;最小未分配原则 每一个进程中有一个task_struct结构体&#xff08;PCB)&#xff0c;而task_struct中含有struct file_sturct*file的指针&#xff0c;该指针指向了一个struct files_struct的结构体该结构体中含有一个f…

win10系统打开Windows更新是空白的如何解决?

最近装wsl的时候&#xff0c;遇到了这个问题。查阅了很多相关资料&#xff0c;发现导致wsl --install安装不了的主要原因都集中在于windows更新组件损坏导致的&#xff0c;经过排查&#xff0c;我的这个组件确实不能够正常使用&#xff0c;可能是因为之前使用了windows激活工具…

网络治理新模式:Web3时代的社会价值重构

随着Web3技术的崛起&#xff0c;传统的网络治理模式正在经历革新&#xff0c;这不仅仅是技术的进步&#xff0c;更是对社会价值观念的挑战和重构。本文将深入探讨Web3时代的网络治理新模式&#xff0c;其背后的技术基础、社会影响以及未来的发展方向。 1. 引言 Web3时代&#…

【机器学习300问】134、什么是主成分分析(PCA)?

假设你的房间堆满了各种各样的物品&#xff0c;书籍、衣服、玩具等等&#xff0c;它们杂乱无章地散落各处。现在&#xff0c;你想要清理房间&#xff0c;但又不想扔掉任何东西&#xff0c;只是希望让房间看起来更整洁&#xff0c;更容易管理。 你开始思考&#xff0c;能否将物品…

Docker之jekins的安装

jekins官网地址&#xff1a;Jenkins Plugins &#xff08;https://plugins.jenkins.io/&#xff09; jekins 的docker 官方地址&#xff1a;https://hub.docker.com/r/jenkins/jenkins jekins 的docker 允许命令文档地址&#xff1a; docker/README.md at master jenkinsci…

Linux源码阅读笔记07-进程管理4大常用API函数

find_get_pid find_get_pid(...)函数功能&#xff1a;根据进程编号获取对应的进程描述符&#xff0c;具体Linux内核源码对应函数设计如下&#xff1a; 获取进程描述符&#xff0c;且描述符的count1&#xff0c;表示进程多一个用户 pid_task pid_task(...)函数功能&#xff1…

通达信短线抄底主升浪幅图指标公式源码

通达信短线抄底主升浪幅图指标公式源码&#xff1a; A1:REF(C,1); A2:SMA(MAX(C-A1,0),5,1)/SMA(ABS(C-A1),5,1)*1000; A3:BARSLAST(REF(CROSS("RSI.RSI1"(6,12,24),"RSI.RSI2"(6,12,24)),1)); A4:A2-LLV(A2,10); A5:(MA(A4,2)*3A4*13)/16; A6:IF(A5>1…

cuda 学习笔记4

一 基本函数 在GPU上开辟空间&#xff0c;无论定义的数据是float还是int ,还是****gpu_int,分配空间的函数都是下面固定的形式 (void**)& 1.函数定义&#xff0c;global void 是配套使用的&#xff0c;是在GPU上定义&#xff0c;也就是GPU上执行&#xff0c;CPU上调用的函数…

关于0xc000007b的一种解决方案

今天我在安装qview并运行时时&#xff0c;遇到了这个问题。 我在网上查找了许多解决方案&#xff0c;但它们大多都说是某些dll缺失或错误引起的。 这些说法应该是正确的&#xff0c;但我用了dll修复工具后&#xff0c;一点用都没有。 后来捣鼓半天后&#xff0c;我发现很可能…

Golang 百题(实战快速掌握语法)_2

返回集合中满足指定条件的最后一个元素 本实验将实现判断给定集合中的元素是否符合&#xff0c;并返回符合的最后一个元素。 知识点 forfmt.Error 适合人群 本课程属于基础课程。需要用户掌握 Go 语言编程基础知识、计算机基础知识和 Linux 环境的基本用法。 许可证 内容…

【可控图像生成系列论文(五)】ControlNet 和 IP-Adapter 之间的区别有哪些?

系列文章目录 【可控图像生成系列论文&#xff08;一&#xff09;】 简要介绍了 MimicBrush 的整体流程和方法&#xff1b;【可控图像生成系列论文&#xff08;二&#xff09;】 就MimicBrush 的具体模型结构、训练数据和纹理迁移进行了更详细的介绍。【可控图像生成系列论文&…

MySQL高级-索引-设计原则小结

文章目录 1、设计原则2、索引小结2.1、索引概述2.2、索引结构2.3、索引分类2.4、索引语法2.5、SQL性能分析2.6、索引使用2.7、索引设计原则 1、设计原则 针对于数据量较大&#xff0c;且查询比较频繁的表建立索引。针对于常作为查询条件&#xff08;where&#xff09;、排序&am…