文档开发者中心
我的

C++11并发三条规则

总结自 Concurrency and lock-free Programming



规则 1 CP.43: Minimize time spent in a critical section


临界区越短越好!


C++ Core Guidelines: Be Aware of the Traps of Condition Variables 这篇文章中有这样一个代码段:


void setDataReady(){
   std::lock_guard<std::mutex> lck(mutex_);
   dataReady = true;                                // (1)
   std::cout << 'Data prepared' << std::endl;
   condVar.notify_one();
}


mutex_ 在函数进入的时候上锁,在函数结束时解锁。这里只有修改 dataReady 的值需要上锁,因为 std::cout 是线程安全的。notify_one 也是线程安全的,是不需要加锁的。


所以,改进版代码的 setDataReady 函数如下:


void setDataReady(){
  {  // Don't remove because of the lifetime of the mutex (1)
       std::lock_guard<std::mutex> lck(mutex_);
       dataReady = true;
  }                                                       (2)
   std::cout << 'Data prepared' << std::endl;
   condVar.notify_one();              
}


(1)的注释最好不要删除,因为后面来接手或者维护这份代码的程序员(特别是比较初级的、对这个使用方法不熟悉的程序员)很可能觉得这个范围限定符是没有什么用的,所以会将其删掉。



规则2 CP.44: Remember to name your lock_guards and unique_locks


一定要给 lock_guard 和 unique_lock 取名,否则你想上锁的代码可能并没有正确得到锁。


unique_lock<mutex>(m1);
lock_guard<mutex> {m2};
lock(m1, m2);


在上面的代码中, lock_guard 和 unique_lock 对象都是临时变量,创建之后立即被删除了。这种情况下,在构造函数中加锁,然后立即在析构函数中解锁(RAII:Garbage Collection: No Thanks)。


下面有一个简单的例子,能够展示 lock_guard 核心的东西:


// myGuard.cpp

#include <mutex>
#include <iostream>

template <typename T>
class MyGuard{
 T& myMutex;
 public:
   MyGuard(T& m):myMutex(m){
     myMutex.lock();
  std::cout << 'lock' << std::endl;
  }
   ~MyGuard(){
  myMutex.unlock();
     std::cout << 'unlock' << std::endl;
  }
};

int main(){

 std::cout << std::endl;

 std::mutex m;
 MyGuard<std::mutex> {m};                        // (1)
 std::cout << 'CRITICAL SECTION' << std::endl;   // (2)
 
 std::cout << std::endl;

}                                                 // (3)


可以预想,(2)这条语句,其实会在没有加锁的情况下执行。运行结果如下:


$ ./a.out

lock
unlock
CRITICAL SECTION



规则 3 Define a mutex together with the data it guards. Use synchronized_value where possible


这里的中心思想是:将 mutex 放入你想保护的数据中。标准写法如下:


struct Record {
   std::mutex m;   // take this mutex before accessing other members
   // ...
};


synchronized_value 暂时还不属于 C++ 标准,但是很快应该会被纳入。用法如下:


class MyClass {
   struct DataRecord {
      // ...
  };
   synchronized_value<DataRecord> data; // Protect the data with a mutex
};


下面是一个示例:


synchronized_value<std::string> s;

std::string readValue()
{
   return *s;
}

void setValue(std::string const& newVal)
{
   *s=newVal;
}

void appendToValue(std::string const& extra)
{
   s->append(extra);
}


上面的所有操作都是线程安全的。


'The basic idea is that synchronized_value<T> stores a value of type T and a mutex. It then exposes a pointer interface, such that derefencing the pointer yields a special wrapper type that holds a lock on the mutex, and that can be implicitly converted to T for reading, and which forwards any values assigned to the assignment operator of the underlying T for writing.'


synchronized_value 的实现应该就是储存了一个 type 和一个 mutex。对外暴露一个指针接口,dereference 指针产生一个特殊的 wrapper 类型,首先获取 mutex 的锁,然后隐式转换为 T 用来读。另外,可以转发任何 “=” 操作符的操作数用来写。


————————————————————————


BY:HW-LH

点赞
收藏
回复
6
分享
举报
浏览2059 发布于2019-10-22 01:57未知归属地
全部评论
最多点赞
最新发布
最早发布

关联阅读:Code Review 最佳实践

6楼回复于2019-10-30 09:00 未知归属地

关联阅读的1和2很有意思啊

5楼回复于2019-10-28 09:22 未知归属地

关联阅读:C++11中condition_variable的陷阱

4楼回复于2019-10-22 02:01 未知归属地

关联阅读:C++11之为何你要尽量使用auto?

3楼回复于2019-10-22 02:00 未知归属地

关联阅读:C++11之啥时候不要用 auto?

2楼回复于2019-10-22 02:00 未知归属地

本文讲述了C++并发三条规则,作者其他文章可查看楼下超链。

1楼回复于2019-10-22 02:00 未知归属地
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

了解社区公约,与您携手共创和谐专业的开发者社区。