标签:c++11 并发 mutex lock_guard
Mutex是C++中最常见的数据保护机制之一,在访问一块共享数据前,lock mutex,在完成对数据的访问后,unlock mutex。线程库当一个特定mutex被某个线程lock后,其它尝试lock同一个mutex的线程都会被挂起指导这个mutex被unlock。这就保证了所有线程看到的数据都是完整的,不会被修改了一部分的数据。
在C++中,通常我们通过创建std::mutex的实例来获得一个mutex,调用它的成员函数lock()来锁住它,调用unlock来解锁,但实际实现时,并不推荐这么写,因为这需要我们记住所有需要调用unlock()的代码分支,包括异常分支。 C++标准库提供了std::lock_guard模版类,它在构造函数做锁住mutex,在析构函数中解锁,下面是一个使用std::lock_guard来保护被多个线程访问的链表的实例。
#include "stdafx.h" #include <list> #include <mutex> #include <algorithm> // 全局变量链表 std::list<int> some_list; // 全局变量mutex std::mutex some_mutex; void add_to_list(int new_value) { // 使用lock_guard来保护链表 std::lock_guard<std::mutex> guard(some_mutex); some_list.push_back(new_value); } bool list_contains(int value_to_find) { // 使用lock_guard来保护链表 std::lock_guard<std::mutex> guard(some_mutex); return std::find(some_list.begin(), some_list.end(), value_to_find) != some_list.end(); } #include <iostream> int main() { add_to_list(42); std::cout << "contains(1)=" << list_contains(1) << ", contains(42)=" << list_contains(42) << std::endl; }
下面一个例子是一个将受保护数据交给用户自定义函数而导致风险的例子:
#include <mutex> class some_data { int a; std::string b; public: void do_something() {} }; class data_wrapper { private: some_data data; std::mutex m; public: template<typename Function> void process_data(Function func) { std::lock_guard<std::mutex> l(m); func(data); } }; some_data* unprotected; void malicious_function(some_data& protected_data) { unprotected = &protected_data; } data_wrapper x; void foo() { x.process_data(malicious_function); unprotected->do_something(); } int main() { foo(); }在这个例子中,process_data kan看起没啥问题,它使用std::lock_gurad来保护数据,但是调用用户定义的函数func意味着foo可以传入malicious_function来绕过保护,而调用do_something()为不对数据进行lock操作。不仅仅如此,这段代码还有其它问题,比如foo() 中调用unprotected->do_something()也需要对数据进行加锁。对正确的mutex执行加锁操作是所有程序员需要做到的,其基本准则是:
不要将指向受保护数据的指针或引用传递到lock控制范围的外部,比如,通过函数返回值,使用另外的指针或引用来存储受保护数据或将它们作为参数传给其它的函数。
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:c++11 并发 mutex lock_guard
原文地址:http://blog.csdn.net/yamingwu/article/details/47379111