C++ :多线程入门到精通

1:C++多线程并发:

实现C++多线程并发程序的思路如下:将任务的不同功能交由多个函数分别实现,创建多个线程,每个线程执行一个函数,一个任务就这样同时分有不同线程执行完毕。

2:创建线程

2.1 :创建线程需要引入头文件 #include<thread>

2.2 : 语句"std::thread th1(proc1);"创建了一个名为th1的线程,并且线程th1开始执行。

实例化std::thread类对象时,至少需要传递函数名作为参数。如果函数为有参函数,如"void proc2(int a,int b)",那么实例化std::thread类对象时,则需要传递更多参数,参数顺序依次为函数名、该函数的第一个参数、该函数的第二个参数,···,如"std::thread th2(proc2,a,b);"

2.3 :当线程启动后,一定要在和线程相关联的std::thread对象销毁前,对线程运用join()或者detach()方法

join()就是再当前线程 中加入另一个线程,使当前线程阻塞,执行刚才加入线程的逻辑

#include <iostream>
#include <string>
#include <cstring>
#include <thread>
#include<windows.h>
#include<chrono>
#include<ctime>


void function_1() {
    // 模拟子线程干活3s,类似Java sleep() 但是 C++ 没有这个函数,只能通过 window的 
    //Sleep()函数,这个函数需要导入<window是.h>包 或者C++ 11中的  thread和chrono
    // Windows.h 包
    //Sleep(3);



    // thread 或者 chrono 包来使线程休眠
    time_t now = time(0); // 获取当前系统时间
    char* dateTime = ctime(&now);

    cout << "hello world C++ 子线程开始干活 time: "<< dateTime<<endl;
    sleep_for(seconds(3));
    time_t now1 = time(0); // 获取当前系统时间
    char* dateTime1 = ctime(&now1);
    cout << "hello world C++ 子线程干活完毕 time: " << dateTime1<<endl;

}


int main() {
    
     std::cout << std::endl;
     thread th1(function_1);
     th1.join();
     cout << "子线程的活已经完成,回到主线程" << endl;
}

结果:

  hello world C++ 子线程开始干活 time: Thu Apr  7 11:20:18 2022

hello world C++ 子线程干活完毕 time: Thu Apr  7 11:20:21 2022

子线程的活已经完成,回到主线程

3:互斥量(锁)使用

        C++ 中多线程除了要牢牢掌握 std::thread 的基本使用方法,我们还需要掌握互斥量(锁)的使用,这是线程同步的一种机制, 再C++ 11中提供了 4种互斥量。

       std::mutex   :  非递归的互斥量

       std:: timed_mutex  :  带超时的 非递归互斥量

      std:: recursive_mutex  :  递归的互斥量

     std::  recursive_timed_mutex  :  带超时的递归互斥量

在实际的开发应用中,我们一般常用的就是  std:: mutex , 它就是一把锁,我们需要对它进行加锁和解锁。

3.1 看个例子

      3.1.1  多线程情况下,没有加 std:: mutex 锁

#include <iostream>
#include <string>
#include <cstring>
#include <thread>
#include<windows.h>
#include<chrono>
#include<ctime>
#include<mutex>



// C++ 多线程互斥量
void func() {
    cout << "entery threadId:" << get_id() << endl;
    // 当前线程休眠6s
    this_thread::sleep_for(seconds(6));
    cout << "leave threadId: " << get_id() << endl;

}

int main() {

    thread t1(func);
    thread t2(func);
    thread t3(func);

    t1.join();
    t2.join();
    t3.join();
}

打印结果  :
   entery threadId:20640
   entery threadId:12312
   entery threadId:11708
   leave threadId: leave threadId: 12312
   leave threadId: 20640
   11708

结论:
1:很显然开辟了三个线程,三个线程分别在执行 fun()任务时,顺序时错乱的,即后面的线程并没有等到前面线程执行完毕,然后再开启自己线程任务

2:如果我们要保持顺序,那么就需要通过 std::mutex (非递归互斥量)来控制

3.1.2  多线程情况下,通过 std:: mutex 加锁 或者 lock_guard<mutex> lock(g_mutex)来加锁

#include <iostream>
#include <string>
#include <cstring>
#include <thread>
#include<windows.h>
#include<chrono>
#include<ctime>
#include<mutex>



std::mutex g_mutex;
void func_mutex() {
    // 方式一 :先加锁,任务执行完毕后要释放锁
    g_mutex.lock();

    // 方式二 :执行完毕后自动释放锁
    // lock_guard<mutex> lock(g_mutex);

    cout << "entery threadId:" << get_id() << endl;
    // 当前线程休眠6s
    this_thread::sleep_for(seconds(6));
    cout << "leave threadId: " << get_id() << endl;
    
    // 释放锁
    g_mutex.unlock();
}

int main() {

    thread t1(func_mutex);
    thread t2(func_mutex);
    thread t3(func_mutex);

    t1.join();
    t2.join();
    t3.join();
}

打印结果  :
   entery threadId:1648
   leave threadId: 1648

   entery threadId:4168
   leave threadId: 4168

   entery threadId:1356
   leave threadId: 1356

结论:
1:显然加锁之后,执行的顺序都是可以预测的,线程严格按照既定的顺序执行业务逻辑

2:如果采用 g_mutex.lock() 加锁的方式 必须再任务执行完毕后,通过 g_mutex.unlock()来解锁
    但是这有一个弊端就是,如果再处理业务逻辑出现异常时,就不能正常释放锁,这样可能会造成
 死锁,所以C++ 给出了另一种方案,可以通过 lock_guard<mutex> lock(g_mutex)来加锁,这个类可以自动释放锁

3.1.3: 看一下 lock_guard类 

lock_guard是类模板,在其构造函数中自动给std::mutex加锁,在退出作用域的时候自动解锁,这样就可以保证std::mutex的正确操作,这也是RAII(获取资源便初始化)技术的体现

template <class _Mutex>
class lock_guard { 
public:
    using mutex_type = _Mutex;
​
    explicit lock_guard(_Mutex& _Mtx) : _MyMutex(_Mtx) 
    { 
        _MyMutex.lock();     //构造函数加锁       
    }
​
    lock_guard(_Mutex& _Mtx, adopt_lock_t) : _MyMutex(_Mtx)
    { 
    }
​
    ~lock_guard() noexcept
    { 
        _MyMutex.unlock();   //析构函数解锁
    }
​
    lock_guard(const lock_guard&) = delete;
    lock_guard& operator=(const lock_guard&) = delete;
​
private:
    _Mutex& _MyMutex;
};

4:条件变量 std::condition_variable 来保持同步

条件变量是C++11提供的另外一种线程同步机制,通过判断条件是否满足,决定是否阻塞线程,当线程执行条件满足的时候就会唤醒阻塞的线程,常与std::mutex配合使用,C++11提供了两种条件变量。

4.1 :条件变量:std:: condition_variable

std::condition_variable,配合std::unique_lock<std::mutex>使用,通过wait()函数阻塞线程;

4.2 : 条件变量:std::condition_variable_any

可以和任意带有lock()unlock()语义的std::mutex搭配使用,比较灵活,但是其效率不及std::condition_variable

5:异步线程

6:thread_local

7:案例(生产者与消费者模型)

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值