个人知识库 个人知识库
首页
关于
  • C语言
  • CPlusPlus
  • Linux
  • PHP
  • Nginx
  • MySQL
  • Redis
  • Docker
  • Kubernetes
  • SRS
阅读
常用工具
  • 分类
  • 标签
  • 归档
GitHub

Agnes001

坚持是一件很伟大的事业
首页
关于
  • C语言
  • CPlusPlus
  • Linux
  • PHP
  • Nginx
  • MySQL
  • Redis
  • Docker
  • Kubernetes
  • SRS
阅读
常用工具
  • 分类
  • 标签
  • 归档
GitHub
  • C语言

  • CPlusPlus

    • 基础特性

    • vs2019设置
    • C++11特性

    • 并发编程

      • 并发编程
      • chrono
      • thread
      • mutex
        • mutex
          • std::mutex类
          • std::recursive_mutex类
          • std::timed_mutex类
          • std::lock_guard类型模板
          • std::unique_lock类型模板
          • std::lock函数模板
          • std::try_lock函数模板
          • 练习
      • condition_varible
      • future
      • atomic
    • 引用
    • 类和对象
    • 友元和运算符重载
    • 继承
    • 继承和多态
    • 模板
    • C++基础总结
    • 类型转换
    • 异常
    • 容器
    • 算法
    • C++程序设计
    • C++ Primer总结
    • 编程技巧
    • 标准库体系结构与内核分析
    • 设计模式
    • cmake配置C++工程
    • libcurl的使用总结
    • web开发框架--drogon
    • log4cplus使用
    • C++数据类型
    • 函数
    • 线程
    • 进程
    • 文件操作
    • 日常问题记录
    • Cpp案例程序
    • 多线程
    • 侯捷c++11新特性
    • 侯捷stl
  • Lua技术栈

  • edoyun

  • 内存管理

  • 数据结构

  • 网络编程

  • Linux

  • 池化技术

  • 操作系统

  • python

  • 编程技术
  • CPlusPlus
Agnes001
2022-07-05

mutex

# mutex

namespace std
{
    //mutex系列类
    class mutex;//基本的mutex类
    class recursive_mutex;//递归 mutex 类。
    class timed_mutex;//定时 mutex 类。
    class recursive_timed_mutex;//定时递归 mutex 类。

    //其他类型
    struct adopt_lock_t;
    struct defer_lock_t;
    struct try_to_lock_t;
    struct once_flag;

    constexpr adopt_lock_t adopt_lock{};
    constexpr defer_lock_t defer_lock{};
    constexpr try_to_lock_t try_to_lock{};

    //lock类
    template<typename LockableType>
    class lock_guard;
    template<typename LockableType>
    class unique_lock;

    //函数
    template<typename LockableType1,typename... LockableType2>
    void lock(LockableType1& m1,LockableType2& m2...);//同时对多个互斥量上锁。
    template<typename LockableType1,typename... LockableType2>
    int try_lock(LockableType1& m1,LockableType2& m2...);//尝试同时对多个互斥量上锁。
    template<typename Callable,typename... Args>
    void call_once(once_flag& flag,Callable func,Args args...);//如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。
}

# std::mutex类

class mutex
{
public:
  mutex(mutex const&)=delete;
  mutex& operator=(mutex const&)=delete;

  constexpr mutex() noexcept;
  ~mutex();

  void lock();
  void unlock();
  bool try_lock();
};
#include <iostream> //std::cout
#include <thread>   //std::thread
#include <mutex>    //std::mutex
#include <chrono>   //std::chrono

volatile int counter(0);        // non-atomic counter
std::mutex mtx;                 // locks access to counter

void attempt_10k_increases() 
{
    for (int i=0; i<100; ++i) 
    {
        if (mtx.try_lock()) 
        {   // only increase if currently not locked:
            ++counter;
            std::this_thread::sleep_for(std::chrono::microseconds(50));
            mtx.unlock();
        }
        else
        {
            std::cout << std::this_thread::get_id() << " failed" << std::endl;
        }
    }
}

int main (int argc, const char* argv[]) 
{
    std::thread threads[10];
    for (int i=0; i<10; ++i)
    {
        threads[i] = std::thread(attempt_10k_increases);
    }

    for (auto& th : threads) 
    {
        th.join();
    }
    
    std::cout << counter << " successful increases of the counter.\n";

    return 0;
}

# std::recursive_mutex类

std::recursive_mutex 允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,std::recursive_mutex 释放互斥量时需要调用与该锁层次深度相同次数的 unlock(),可理解为 lock() 次数和 unlock() 次数相同,除此之外,std::recursive_mutex 的特性和 std::mutex 大致相同。

# std::timed_mutex类

class timed_mutex
{
public:
  timed_mutex(timed_mutex const&)=delete;
  timed_mutex& operator=(timed_mutex const&)=delete;

  timed_mutex();
  ~timed_mutex();

  void lock();
  void unlock();
  bool try_lock();

  //在指定的relative_time时间内,尝试获取锁。
  template<typename Rep,typename Period>
  bool try_lock_for(std::chrono::duration<Rep,Period> const& relative_time);
  //时间点 chrono::system_clock.now()+timeout
  template<typename Clock,typename Duration>
  bool try_lock_until(std::chrono::time_point<Clock,Duration> const& absolute_time);
};

# std::lock_guard类型模板

template <class Mutex>
class lock_guard
{
public:
  typedef Mutex mutex_type;

  explicit lock_guard(mutex_type& m);
  lock_guard(mutex_type& m, adopt_lock_t);
  ~lock_guard();

  lock_guard(lock_guard const& ) = delete;
  lock_guard& operator=(lock_guard const& ) = delete;
};

指定的互斥量在构造函数中上锁,在析构函数中解锁。这就为互斥量锁部分代码提供了一个简单的方式;当程序运行完成时,阻塞解除,互斥量解锁(无论是执行到最后,还是通过控制流语句break或return,亦或是抛出异常)。

std::mutex g_mtx;
void threadfunc(char ch)
{
    //首先,这在一个局部作用域内, std::lock_guard 在构造时,
    //会调用 g_mutex->lock() 方法;
    std::lock_guard<std::mutex> lock(g_mtx);    
    for (int i = 0; i < 5; ++i)
    {   
        //std::lock_guard<std::mutex> lock(g_mtx);     //加锁
        for (int j = 0; j < 5; ++j)
        { // std::lock_guard<std::mutex> lock(g_mtx); 
            printf("i=%d,j=%d %c", i,j,ch);
        }
        printf("\n");
    }
    printf("\n");
    //局部作用域代码结束后,
    //std::lock_guard 的析构函数会被调用,
    //函数中会调用 g_mutex->unlock() 方法。
}

int main()
{
    char ch = 'a';
    std::thread that[5];
    for (int i = 0; i < 5; ++i)
    {
         that[i] = std::thread(threadfunc, ch + i);
    }
    for (int i = 0; i < 5; ++i)
    {
       that[i].join();
    }
   cout << "Main End" << endl;
   return 0;
}

# std::unique_lock类型模板

临时加解锁

void shared_print(string msg, int id) {
    std::unique_lock<std::mutex> guard(_mu, std::defer_lock);//延时加锁
    //do something 1

    guard.lock();
    // do something protected
    guard.unlock(); //临时解锁

    //do something 2

    guard.lock(); //继续上锁
    // do something 3
    f << msg << id << endl;
    cout << msg << id << endl;
    // 结束时析构guard会临时解锁
}

通过std::defer_lock设置初始化的时候不进行默认的上锁操作。在无需加锁的操作时,可以先临时释放锁,然后需要继续保护的时候,可以继续上锁,这样就无需重复的实例化lock_guard对象,还能减少锁的区域。

# std::lock函数模板

std::lock函数模板提供同时锁住多个互斥量的功能,且不会有因改变锁的一致性而导致的死锁。

# std::try_lock函数模板

std::try_lock函数模板允许尝试获取一组可锁对象上的锁,所以要不全部获取,要不一个都不获取。

# 练习

c++多线程中死锁多出现在使用多个mutex,而mutex的顺序不一样。比如下面的代码为了保证输出的原子性,在两个输出的函数中使用了两个mutex,但是这两个mutex锁的顺序是不一样的,结果就造成了死锁。

#include<iostream>
#include<thread>
#include<mutex>
#include<string>
#include<fstream>

class LofFile
{
public:

	void share_print(std::string msg, int id) {
		std::lock_guard<std::mutex> guard(m_mutex);
		std::lock_guard<std::mutex> guard2(m_mutex2);
		std::cout << msg << " " << id << std::endl;
	}

	void share_print_f(std::string msg, int id) {
		std::lock_guard<std::mutex> guard(m_mutex2);
		std::lock_guard<std::mutex> guard2(m_mutex);
		std::cout << msg << " " << id << std::endl;
	}
private:
	std::mutex m_mutex;
	std::mutex m_mutex2;
};

void fun1(LofFile& log)
{
	for (int i = 0; i < 50; i++)
	{
		log.share_print("fun1 id", i);
	}
}

int main(int argc, char** argv)
{
	LofFile log;
	std::thread t1(fun1, std::ref(log));
	for (int i = 0; i < 50; i++)
	{
		log.share_print_f("main id", i);
	}
	if (t1.joinable()) {
		t1.join();
	}
	return 0;
}

如何避免在C++中使用mutex出现死锁 1、判断自己的代码是否需要多个mutex 2、清楚了解自己调用的方法之类的是否也有使用mutex的情况 3、使用标准库中的std::Lock(std::mutex,···)来设置mutex

如何使用std::Lock(std::mutex,···)

class LofFile
{
public:

	void share_print(std::string msg, int id) {
		std::lock(m_mutex, m_mutex2);
		std::lock_guard<std::mutex> guard(m_mutex,std::adopt_lock);
		std::lock_guard<std::mutex> guard2(m_mutex2,std::adopt_lock);
		std::cout << msg << " " << id << std::endl;
	}

	void share_print_f(std::string msg, int id) {
		std::lock(m_mutex, m_mutex2);
		std::lock_guard<std::mutex> guard(m_mutex2,std::adopt_lock);
		std::lock_guard<std::mutex> guard2(m_mutex,std::adopt_lock);
		std::cout << msg << " " << id << std::endl;
	}
private:
	std::mutex m_mutex;
	std::mutex m_mutex2;
};

主要修改了两个地方 调用std::lock_guardstd::mutex 前先使用std::lock(std::mutex,···), 其次在std::lock_guardstd::mutex 中加入std::adopt_lock参数

编辑此页
thread
condition_varible

← thread condition_varible →

Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式