c++语言级别的多线程编程

通过语言级别的多线程编程可以使多线程代码跨平台运行

使用线程时尽量脱离

thread/mutex/condition_variable

lock_quead/unique_lock

atomic基于CAS操作的原子类型

睡眠sleep_for

多线程基础

#include<iostream>
#include<thread>

//this_thread是当前线程下的一个命名空间,用于调用该线程的一些基本信息和函数
//chrono也是一个std下的命名空间,里面有一些常见的时间函数
void threadHanler1(int seconds){
    std::cout<<"Hello Thread"<<std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(seconds));
    std::cout<<"GoodBye Thread"<<std::endl;
}

signed main(){
    //定义了一个线程对象 , 传递了一个线程入口函数
    //传入后新线程就开始运行了
    std::thread t1(threadHanler1 , 1);

    //主线程等待子进程继续之后就继续往下运行
    t1.join();

    //把子线程设置为分离线程
    t1.detach();

    std::cout<<"finish!"<<std::endl;

    //主线程在运行结束的时候会检查所有子线程
    //如果子线程不是分离线程,且未结束,就会抛出异常
    return 0;
}

主线程在运行结束的时候会检查所有子线程
如果子线程不是分离线程,且未结束,就会抛出异常

线程互斥

如何防止死锁问题的发生:使用lock_guard和unique_lock

/*
 * @Date: 2024-08-01 02:41:45
 * @LastEditors: cosh
 * @LastEditTime: 2024-08-08 15:23:14
 * @FilePath: \c++\a.cpp
 */
#include<iostream>
#include<thread>
#include<list>
#include<mutex>
using namespace std;

/*
竞态条件:多线程程序执行的结果应该是一致的,不会随着CPU对线程不同的调用顺序,而产生不同的运行结果
*/

//模拟卖票程序(存在竞态条件)【使用互斥锁】
int ticketCount = 100;
std::mutex mtx;

//售票窗口
void sellTicket(int index){
    while(ticketCount > 0){
        
        
        //问题:如果在if中子线程异常退出,锁没有及时释放,导致死锁 -> 使用智能指针
        //lock_guard 和 unique_lock
        {
            //lock_guard<std::mutex> lock(mtx);   //有点像scored_ptr , ban了赋值和拷贝构造 , 会自动加锁(建议只用这个)
            unique_lock<std::mutex> lock(mtx);  //支持带右值引用的拷贝构造和赋值运算符重载
            lock.lock();
            if(ticketCount > 0){
                cout<<"窗口"<<index<<"卖出第 : " << ticketCount <<"张票"<<endl;
                ticketCount--;
            }
            lock.unlock();
        }   //新建一个作用域,出了作用域之后就自动析构掉了
        
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

signed main(){
    list<std::thread>  tlist;
    for(int i = 1 ; i <= 3 ; i++){
        tlist.push_back(std::thread(sellTicket , i)); 
    }

    for(std::thread &t : tlist){
        t.join();
    }

    return 0;  
}

基于CAS操作的atomic原子类型

互斥锁的操作是比较重的,临界区代码做的事情稍稍复杂

系统理论上:CAS来保证线面的++ --操作就足够了 CAS操作又称为无锁操作

面试题,如何实现一个无锁队列

利用CAS在硬件层面加锁

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

volatile std::atomic_bool isReady = false;
volatile std::atomic_int ticketCount = 0;
//防止子线程对对象进行缓存,保证变量一定是原始数据的值

void task(){
    while(!isReady){
        std::this_thread::yield();  //线程出让当前的CPU时间片,等待下一次调度
    }

    for(int i = 0 ; i < 100 ; i++){
        ticketCount++;
    }
}

signed main(){
    list<std::thread> tlist;

    for(int i = 1 ; i <= 10 ; i++){
        tlist.push_back(thread(task));
    }

    std::this_thread::sleep_for(std::chrono::seconds(3));
    isReady = true;
    cout<<"ticketCount:"<<ticketCount<<endl;

    for(std::thread &t : tlist) t.join();
}

c++线程间同步通信

多线程编程的两个问题:

  1. 线程间的互斥

    竞态条件 -> 临界区代码段 -> 原子操作 -> 互斥锁mutex / 轻量级的无锁机制(CAS)

    strace ./a.out -> pthread_mutex_t

  2. 线程间的同步通信

    不通信的话就没有执行顺序可言,出问题的时候哪里复现

    生产者,消费者线程模型

    C++的所有容器都不是线程安全的

文章作者: cosh
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 cosh'blog
C++
喜欢就支持一下吧