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";
}
相关推荐
懒惰的bit2 小时前
基础网络安全知识
学习·web安全·1024程序员节
SRY122404192 小时前
javaSE面试题
java·开发语言·面试
李元豪3 小时前
【智鹿空间】c++实现了一个简单的链表数据结构 MyList,其中包含基本的 Get 和 Modify 操作,
数据结构·c++·链表
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
UestcXiye3 小时前
《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项
c++·计算机网络·ip·tcp
一丝晨光4 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
代码小鑫4 小时前
A027-基于Spring Boot的农事管理系统
java·开发语言·数据库·spring boot·后端·毕业设计