C++多线程学习03

参考

主要参考B站up主恋恋风辰:恋恋风辰官方博客

测试加锁

cpp 复制代码
#include <iostream>
#include <mutex>
#include <map>
#include <stack>
#include <shared_mutex>

// 基本的加锁使用
std::mutex mtx1;

void use_lock()
{
    int count = 10;
    while (count--)
    {
#if 0
        mtx1.lock(); 
        shared_data++;
        std::cout << "current thread is " << std::this_thread::get_id() << std::endl;
        std::cout << "sharad data is " << shared_data << std::endl;
        mtx1.unlock();
#endif
        // 直接使用lock_guard加锁, 程序结束自动释放
        std::lock_guard<std::mutex> lock(mtx1);
        shared_data++;
        std::cout << "current thread is " << std::this_thread::get_id() << std::endl;
        std::cout << "sharad data is " << shared_data << std::endl;
        std::this_thread::sleep_for(std::chrono::microseconds(10));
    }
}

void test_lock()
{
    std::thread t1(use_lock);
    std::thread t2([]() {
        int count = 10;
        while (count--) {
            mtx1.lock();
            shared_data--;
            std::cout << "current thread is " << std::this_thread::get_id() << std::endl;
            std::cout << "sharad data is " << shared_data << std::endl;
            mtx1.unlock();
            std::this_thread::sleep_for(std::chrono::microseconds(10));
        }
    });

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

int main()
{
    // 1. 测试加锁
    test_lock();

    std::cout << "Finished! \n";
}

测试线程安全数据结构1

cpp 复制代码
// 保证数据安全
// 1. 错误实现
template<typename T>
class threadsafe_stack1
{
private:
    std::stack<T> data;
    mutable std::mutex m;
public:
    threadsafe_stack1() {}
    threadsafe_stack1(const threadsafe_stack1& other)
    {
        std::lock_guard<std::mutex> lock(other.m);
        //①在构造函数的函数体(constructor body)内进行复制操作
        data = other.data;
    }
    threadsafe_stack1& operator=(const threadsafe_stack1&) = delete;
    void push(T new_value)
    {
        std::lock_guard<std::mutex> lock(m);
        data.push(std::move(new_value));
    }
    //问题代码
    T pop()
    {
        std::lock_guard<std::mutex> lock(m);
        auto element = data.top();
        data.pop();
        return element;
    }
    bool empty() const
    {
        std::lock_guard<std::mutex> lock(m);
        return data.empty();
    }
};

void test_threadsafe_stack1() {
    threadsafe_stack1<int> safe_stack;
    safe_stack.push(1);
    
    // 两个线程同时抛出,可能存在隐患,相当于pop了两次
    std::thread t1([&safe_stack]() {
        if (!safe_stack.empty()) {
            std::this_thread::sleep_for(std::chrono::seconds(2));
            safe_stack.pop();
        }
        });
    std::thread t2([&safe_stack]() {
        if (!safe_stack.empty()) {
            std::this_thread::sleep_for(std::chrono::seconds(2));
            safe_stack.pop();
        }
        });
    t1.join();
    t2.join();
}

// 重新实现的线程安全栈
struct empty_stack : std::exception
{
    const char* what() const throw();
};


template<typename T>
class threadsafe_stack
{
private:
    std::stack<T> data;
    mutable std::mutex m;
public:
    threadsafe_stack() {}
    threadsafe_stack(const threadsafe_stack& other)
    {
        std::lock_guard<std::mutex> lock(other.m);
        
        //①在构造函数的函数体(constructor body)内进行复制操作
        data = other.data;
    }
    threadsafe_stack& operator=(const threadsafe_stack&) = delete;
    void push(T new_value)
    {
        std::lock_guard<std::mutex> lock(m);
        data.push(std::move(new_value));
    }
    std::shared_ptr<T> pop()
    {
        std::lock_guard<std::mutex> lock(m);
        
        // 试图弹出前检查是否为空栈
        if (data.empty()) throw empty_stack();
        
        // 改动栈容器前设置返回值
        // 操作智能指针效率更高一些
        std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
        data.pop();
        return res;
    }
    void pop(T& value)
    {
        std::lock_guard<std::mutex> lock(m);
        if (data.empty()) throw empty_stack();
        value = data.top();
        data.pop();
    }
    bool empty() const
    {
        std::lock_guard<std::mutex> lock(m);
        return data.empty();
    }
};

int main()
{
    // 2. 测试线程线程安全
    // test_threadsafe_stack1();

    std::cout << "Finished! \n";
}

死锁以及解决方案

cpp 复制代码
// 关于死锁
// 加锁的顺序不对,互相占有对方的锁
// 解决方案1: 将加锁,解锁封装为独立的函数, 这样一个函数内不会使用多个锁

std::mutex t_lock1;
std::mutex t_lock2;

int m_1, m_2;

void atomic_lock1()
{
    std::cout << "Lock1 begin lock" << std::endl;
    t_lock1.lock();
    m_1 = 1024;
    t_lock1.unlock();
    std::cout << "Lock1 end lock" << std::endl;
}

void atomic_lock2()
{
    std::cout << "Lock2 begin lock" << std::endl;
    t_lock2.lock();
    m_2 = 2048;
    t_lock2.unlock();
    std::cout << "Lock2 end lock" << std::endl;
}

void safe_lock1()
{
    int count = 10;
    while (count--) {
        atomic_lock1();
        atomic_lock2();
        std::this_thread::sleep_for(std::chrono::microseconds(5));
    }
}

void safe_lock2()
{
    int count = 10;
    while (count--) {
        atomic_lock1();
        atomic_lock2();
        std::this_thread::sleep_for(std::chrono::microseconds(5));
    }
}

void test_safe_lock()
{
    std::thread t1(safe_lock1);
    std::thread t2(safe_lock2);
    t1.join();
    t2.join();
}

int main()
{
    // 3. 死锁
    // 解决方案1
    test_safe_lock();

    std::cout << "Finished! \n";
}

测试交换两个特别大的对象

cpp 复制代码
// 同时加锁
class some_big_obj
{
public:
    some_big_obj(int data): _data(data){}

    // 拷贝构造
    some_big_obj(const some_big_obj& b) : _data(b._data)
    {
        _data = b._data;
    }

    // 移动构造
    some_big_obj(some_big_obj&& b): _data(std::move(b._data)){}

    // 重载运算符
    friend std::ostream& operator << (std::ostream& os, const some_big_obj& big_obj)
    {
        os << big_obj._data;
        return os;
    }

    // 重载赋值运算符
    some_big_obj& operator = (const some_big_obj& b)
    {
        if (this == &b) return *this;
        _data = b._data;
        return *this;
    }

    // 交换数据
    friend void swap(some_big_obj& b1, some_big_obj& b2)
    {
        some_big_obj temp = std::move(b1);
        b1 = std::move(b2);
        b2 = std::move(temp);
    }

private:
    int _data;
};

// 实现一个管理者class
class big_obj_manager
{
private:
    std::mutex      _mtx;
    some_big_obj    _obj;

public:
    big_obj_manager(int data = 0): _obj(data){}

    void printInfo()
    {
        std::cout << "Curr obj data is " << _obj << std::endl;
    }

    friend void danger_swap(big_obj_manager& objm1, big_obj_manager& objm2);
    friend void safe_swap(big_obj_manager& objm1, big_obj_manager& objm2);
    friend void safe_swap_scope(big_obj_manager& objm1, big_obj_manager& objm2);
};

// 危险的交换方式1
void danger_swap(big_obj_manager& objm1, big_obj_manager& objm2) {
    std::cout << "thread [ " << std::this_thread::get_id() << " ] begin" << std::endl;
    if (&objm1 == &objm2) {
        return;
    }
    std::lock_guard <std::mutex> gurad1(objm1._mtx);
    //此处为了故意制造死锁,我们让线程小睡一会
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::lock_guard<std::mutex> guard2(objm2._mtx);
    swap(objm1._obj, objm2._obj);
    std::cout << "thread [ " << std::this_thread::get_id() << " ] end" << std::endl;
}

void test_danger_swap()
{
    big_obj_manager objm1(5);
    big_obj_manager objm2(100);

    std::thread t1(danger_swap, std::ref(objm1), std::ref(objm2));
    std::thread t2(danger_swap, std::ref(objm1), std::ref(objm2));
    t1.join();
    t2.join();

    objm1.printInfo();
    objm2.printInfo();
}

// 为了避免死锁,同时对两个锁加锁
void safe_swap(big_obj_manager& objm1, big_obj_manager& objm2)
{
    std::cout << "thread [ " << std::this_thread::get_id() << " ] begin" << std::endl;
    if (&objm1 == &objm2) {
        return;
    }

    // 同时对两个互斥量加锁
    std::lock(objm1._mtx, objm2._mtx);

    // 使用领养锁管理自动释放
    std::lock_guard<std::mutex> gurad1(objm1._mtx, std::adopt_lock);
    // 领养后会自动释放

    std::this_thread::sleep_for(std::chrono::seconds(1));

    std::lock_guard<std::mutex> gurads(objm2._mtx, std::adopt_lock);
    swap(objm1._obj, objm2._obj);
    std::cout << "thread [ " << std::this_thread::get_id() << " ] end" << std::endl;
}

void test_safe_swap()
{
    big_obj_manager objm1(5);
    big_obj_manager objm2(100);

    std::thread t1(safe_swap, std::ref(objm1), std::ref(objm2));
    std::thread t2(safe_swap, std::ref(objm2), std::ref(objm1));
    t1.join();
    t2.join();

    objm1.printInfo();
    objm2.printInfo();
}

int main()
{
#if 0
    // 危险交换测试
    test_danger_swap();
#endif
    // 安全的交换方式
    test_safe_swap();

    std::cout << "Finished! \n";
}

层级锁避免死锁

cpp 复制代码
// 实际开发中,锁的顺序可能很难注意,可以使用层级锁避免死锁
class hierarchical_mutex
{
private:
    std::mutex              _internal_mutex;
    unsigned long const     _hierarchy_value;            // 当前层级值
    unsigned long           _previous_hierarchy_value;   // 上一层级值
    static thread_local unsigned long _this_thread_hierarchy_value;     // 记录本线程层级值

    void check_for_hierarchy_violation()
    {
        if (_this_thread_hierarchy_value <= _hierarchy_value)
        {
            throw std::logic_error("mutex hierarchy violated");
        }
    }

    void update_hierachy_value()
    {
        _previous_hierarchy_value = _this_thread_hierarchy_value;
        _this_thread_hierarchy_value = _hierarchy_value;
    }
    
public:
    explicit hierarchical_mutex(unsigned long value): _hierarchy_value(value), _previous_hierarchy_value(0){}
    hierarchical_mutex(const hierarchical_mutex&) = delete;
    hierarchical_mutex& operator=(const hierarchical_mutex&) = delete;

    void lock()
    {
        check_for_hierarchy_violation();
        _internal_mutex.lock();
        update_hierachy_value();
    }

    void unlock()
    {
        if (_this_thread_hierarchy_value != _hierarchy_value)
            throw std::logic_error("mutex hierachy violated");

        _this_thread_hierarchy_value = _previous_hierarchy_value;
        _internal_mutex.unlock();
    }

    bool try_lock()
    {
        check_for_hierarchy_violation();
        if (!_internal_mutex.try_lock()) {
            return false;
        }

        update_hierachy_value();
        return false;
    }
};

// 初始化静态变量
thread_local unsigned long hierarchical_mutex::_this_thread_hierarchy_value(ULONG_MAX);

// 测试层级锁
void test_hierarchy_lock()
{
    hierarchical_mutex      hmtx1(1000);
    hierarchical_mutex      hmtx2(500);

    std::thread t1([&hmtx1, &hmtx2]() {
        hmtx1.lock();
        hmtx2.lock();
        hmtx2.unlock();
        hmtx1.unlock();
    });

#if 0
    // 以下是错误代码
    std::thread t2([&hmtx1, &hmtx2]() {
        hmtx2.lock();
        hmtx1.lock();
        hmtx1.unlock();
        hmtx2.unlock();
    });
#endif
    t1.join();
    // t2.join();
}

int main()
{
    // 4. 实现一个层级锁
    test_hierarchy_lock();

    std::cout << "Finished! \n";
}
相关推荐
Scott9999HH4 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
2401_841495645 小时前
【操作系统】进程同步与互斥实验报告
c++·算法·操作系统·进程·并发·同步·互斥
fqbqrr5 小时前
2607C++,soui与安卓
c++·soui
码智社5 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海5 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖6 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a1117768 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
我的xiaodoujiao8 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest
fqbqrr8 小时前
2607C++,使用微软detours勾挂工具
c++