C++学习(线程相关)

目录

一、线程库thread

1.使用外部函数

[2. 使用类的函数](#2. 使用类的函数)

[3. 添加参数](#3. 添加参数)

[二、线程库 mutex](#二、线程库 mutex)

1.使用lock()方法

2.try_lock()方法

三、线程库lock_guard

四、线程库unique_lock

1.adopt_lock

2.defer_lock()

五、线程库call_once

[六、线程库promise & future](#六、线程库promise & future)

七、condition变量使用场景

[八、async 和 packaged_task](#八、async 和 packaged_task)

1.async

2.packaged_task


一、线程库thread

1.使用外部函数

下面这段代码的目的是等待子线程运行结束,因为可能存在主线程已经结束了,但是子线程还有程序要运行,此时直接return可能会出问题。

cpp 复制代码
    t1.join();

2. 使用类的函数

cpp 复制代码
#include<iostream>
#include<chrono>
#include<thread>
using namespace std;

int i = 0;
void test1()
{
    while(i< 10)
    {
        cout << "子线程运行中:" << i << endl;
        i++;
        this_thread::sleep_for(chrono::microseconds(10));
    }
}
class A
{
    private:
        int i = 0;
    public:
    void test2()
    {
        while(i< 10)
        {
            cout << "子线程运行中:" << i << endl;
            i++;
            this_thread::sleep_for(chrono::microseconds(10));
        }
    }
};

int main()
{
    A a;
    thread t1(&A::test2, &a);
    while(i< 10)
    {
        cout << "主线程运行中:" << i << endl;
        i++;
        this_thread::sleep_for(chrono::microseconds(10));
    }
    t1.join();
    return 0;
}

3. 添加参数

cpp 复制代码
#include<iostream>
#include<chrono>
#include<thread>
using namespace std;
int i = 0;
class A
{
    private:
        int i = 0;
    public:
    void test2(int num)
    {
        while(i< 10)
        {
            cout << "子线程运行中:num:" << num << endl;
            this_thread::sleep_for(chrono::microseconds(10));
            i++;
        }
    }
};

int main()
{
    A a;
    thread t1(&A::test2, &a, 100);
    while(i< 10)
    {
        cout << "主线程运行中:" << i << endl;
        i++;
        this_thread::sleep_for(chrono::microseconds(10));
    }
    t1.join();
    return 0;
}

二、线程库 mutex

对于上面第一个的例子,存在主线程或者子线程打印一半,时间片结束,进入另外一个线程打印,导致乱码,为了解决这个问题,引入锁。

1.使用lock()方法

使用lock加锁之后,当子线程执行一般,此时还没释放锁,进入主线程,主线程也调用lock(),发现锁已经被占用了,于是就等待,之后时间片到了进入子线程,子线程程序执行完毕后释放锁unlock()。

2.try_lock()方法

这个方法尝试获取锁,成功获得返回true,否则返回false,他不会阻塞,获取不到锁也可以做一些其他操作。

cpp 复制代码
#include<iostream>
#include<chrono>
#include<thread>
#include<mutex>
using namespace std;
int i = 0;
mutex mtx;
void test2()
{
    while(i< 10)
    {
        if(mtx.try_lock())
        {
        cout << "子线程运行中:" << i << endl;
        this_thread::sleep_for(chrono::microseconds(10000));
        i++;
        mtx.unlock();
        }
        else
        {
            cout << "子线程等待中:"<< endl;
        }
    }
}
int main()
{
    thread t1(test2);
    while(i< 10)
    {
        if(mtx.try_lock())
        {
            cout << "主线程运行中:" << i << endl;
            i++;
            this_thread::sleep_for(chrono::microseconds(10000));
            mtx.unlock();
        }
        else
        {
            cout << "主线程等待中:"<< endl;
        }
    }
    t1.join();
    return 0;
}

三、线程库lock_guard

上面每次都需要手动释放,可能存在忘了释放锁,就会有bug,下面解决这个问题。本质上当这个变量作用域结束的时候执行析构函数,自动释放锁。

cpp 复制代码
#include<iostream>
#include<chrono>
#include<thread>
#include<mutex>
#include<sstream>
using namespace std;

int i = 0;
mutex mtx;

// 辅助函数,用于获取线程 ID 字符串
string getThreadId() {
    stringstream ss;
    ss << this_thread::get_id();
    return ss.str();
}

void test2()
{
    while(i < 10)
    {
        lock_guard<mutex> lock(mtx);
        cout << "子线程 " << getThreadId() << " 运行中:" << i << endl;
        this_thread::sleep_for(chrono::microseconds(10));
        i++;
    }
}

int main()
{
    thread t1(test2);
    while(i < 10)
    {
        lock_guard<mutex> lock(mtx);
        cout << "主线程 " << getThreadId() << " 运行中:" << i << endl;
        i++;
        this_thread::sleep_for(chrono::microseconds(10));
    }
    t1.join();
    return 0;
}

四、线程库unique_lock

unique_lock提供了更高级的用法。它可以多传一个参数,也可以不传,不穿的用法和lock_guard一样。

1.adopt_lock

它的作用是接管锁,上面先创建一个锁,之后由他接管,作用域结束的时候自动释放。

cpp 复制代码
#include<iostream>
#include<chrono>
#include<thread>
#include<mutex>
#include<sstream>
using namespace std;

int i = 0;
mutex mtx;

// 辅助函数,用于获取线程 ID 字符串
string getThreadId() {
    stringstream ss;
    ss << this_thread::get_id();
    return ss.str();
}

void test2()
{
    while(i < 10)
    {
        mtx.lock();
        unique_lock<mutex> lock(mtx, adopt_lock);
        cout << "子线程 " << getThreadId() << " 运行中:" << i << endl;
        this_thread::sleep_for(chrono::microseconds(10));
        i++;
    }
}

int main()
{
    thread t1(test2);
    while(i < 10)
    {
        mtx.lock();
        unique_lock<mutex> lock(mtx, adopt_lock);
        cout << "主线程 " << getThreadId() << " 运行中:" << i << endl;
        i++;
        this_thread::sleep_for(chrono::microseconds(10));
    }
    t1.join();
    return 0;
}

2.defer_lock()

defer_lock的作用是延迟锁,在后面调用lock.lock();才会获取锁和加锁。

cpp 复制代码
#include<iostream>
#include<chrono>
#include<thread>
#include<mutex>
#include<sstream>
using namespace std;

int i = 0;
mutex mtx;

// 辅助函数,用于获取线程 ID 字符串
string getThreadId() {
    stringstream ss;
    ss << this_thread::get_id();
    return ss.str();
}

void test2()
{
    while(i < 10)
    {
        unique_lock<mutex> lock(mtx, defer_lock);
        lock.lock();
        cout << "子线程 " << getThreadId() << " 运行中:" << i << endl;
        this_thread::sleep_for(chrono::microseconds(10));
        i++;
    }
}

int main()
{
    thread t1(test2);
    while(i < 10)
    {
        unique_lock<mutex> lock(mtx, defer_lock);
        lock.lock();
        cout << "主线程 " << getThreadId() << " 运行中:" << i << endl;
        i++;
        this_thread::sleep_for(chrono::microseconds(10));
    }
    t1.join();
    return 0;
}

五、线程库call_once

如果存在好几个线程,调用一个函数,这个函数里面有个初始化操作,这个操作只需要被执行一次,就会用到下面的操作。

声明once_flag, 之后将once_flag,init函数和以及init函数的参数传递给call_once。

六、线程库promise & future

这个主要作用是异步获取线程函数里面的数据。int sum = ft.get();这段代码会阻塞,直到子线程设置ps.set_value(sum);。注意使用future<int> ft = ps.get_future();声明的话,主线程只能get一次,如果想要get多次,那么使用shared_future<int> ft = ps.get_future();。

七、condition变量使用场景

生产者消费者模型,生产者不断往队列中添加任务,之后通知消费者取任务。

关键代码解释

cv.notify_one(); // 通知另外一个线程取任务

cv.wait(lck, []{return !q.empty();}); // 这个锁的参数很有必要,因为上面给锁住了,所以需要先释放了锁才行,所以锁也需要作为参数传入,后面的就是条件,如果为True就等待。

cpp 复制代码
#include<iostream>
#include<chrono>
#include<thread>
#include<mutex>
#include<queue>
#include<condition_variable>
using namespace std;


queue<int> q;
condition_variable cv;
mutex mtx;
void Product()
{
    for(int i=0; i<10; i++)
    {
        {
            unique_lock<mutex> lck(mtx);
            q.push(i);
            cv.notify_one();
            cout << "生产了" << i << endl << flush; 
        }
        this_thread::sleep_for(chrono::milliseconds(5)); // 延长时间
    }
}

void Consumer()
{
    while(true)
    {
        unique_lock<mutex> lck(mtx);
        cv.wait(lck, []{return !q.empty();});
        int data = q.front();
        q.pop();
        cout << "消费了" << data << ",队列大小: " << q.size() << endl << flush;

        if(data == -1) break;  // 正确退出
        this_thread::sleep_for(chrono::milliseconds(3)); 
    }
}
void test2()
{   
    thread t2(Consumer);
    thread t1(Product);
    t1.join();
    // 添加退出条件,避免消费者线程无限循环
    {
        unique_lock<mutex> lck(mtx);
        q.push(-1); // 发送结束信号
        cv.notify_one();
    }
    t2.join();
}

int main()
{
    test2();
    return 0;
}

八、async 和 packaged_task

他俩的作用都是异步获取函数返回值。

1.async

关键代码解释:

future<int> f = async(add); // 自动产生一个线程执行add这个函数

f.get() // 读取函数的返回值

2.packaged_task

关键代码解释:

packaged_task<int()> task(add); // 创建一个packaged_task对象,不会创建线程

auto future_result = task.get_future(); // 得到这个任务的future对象

thread t(move(task)); // 根据任务创建线程,开始这行任务里面的函数

cout<<"sum = "<<future_result.get()<<endl; // 等待函数执行完成,获得返回值,对于future的get方法,如果函数没有执行完成,就会阻塞。

相关推荐
老虎062728 分钟前
JavaWeb(苍穹外卖)--学习笔记13(微信小程序开发,缓存菜品,Spring Cache)
笔记·学习·微信小程序
@蓝莓果粒茶2 小时前
LeetCode第350题_两个数组的交集II
c++·python·学习·算法·leetcode·职场和发展·c#
无名工程师2 小时前
AI 学习过程中各阶段的学习重点、时间规划以及不同方向的选择与建议等内容
人工智能·学习
笠码2 小时前
JVM Java虚拟机
java·开发语言·jvm·垃圾回收
试着2 小时前
零基础学习性能测试第五章:JVM性能分析与调优-垃圾回收器的分类与回收
jvm·学习·零基础·性能测试·垃圾回收器
livemetee2 小时前
Flink2.0学习笔记:Stream API 常用转换算子
大数据·学习·flink
WXX_s2 小时前
【OpenCV篇】OpenCV——03day.图像预处理(2)
人工智能·python·opencv·学习·计算机视觉
艾莉丝努力练剑3 小时前
【LeetCode&数据结构】二叉树的应用(二)——二叉树的前序遍历问题、二叉树的中序遍历问题、二叉树的后序遍历问题详解
c语言·开发语言·数据结构·学习·算法·leetcode·链表
kk在加油3 小时前
全面理解JVM虚拟机
jvm
花月mmc3 小时前
CanMV-K230 AI学习笔记系列
人工智能·笔记·学习