并发 多线程

目录

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

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值