1,boosy::dynamic_bitset介绍
bitset与vector<bool> 类似,同样存储二进制位,但它的大小固定,而且比vector<bool>支持更多的位运算。
vector<bool〉和bitset 各有优缺点: vector<bool>可以动态增长,但不能方便地进行位运算; bitset 则正好相反,可以方便地对容纳的二进制位做位运算,但不能动态增长。
boost.dynamic_bitset的出现恰好填补了这两者之间的空白,它类似标准库的bitset,提供丰富的位运算,同时长度又是动态可变的。
void test_dynamic_bitset()
{
using namespace boost;
// 1. 构造
dynamic_bitset<> db1; // 空的dynamic_bitset
dynamic_bitset<> db2(10); // 大小为10的dynamic_bitset
dynamic_bitset<> db3('Ox16', BOOST_BINARY(10101)); //
dynamic_bitset<> db4(std::string("0101")); // 字符串构造
// 2. resize
db1.resize(8, true);
assert(db1.to_ulong() == BOOST_BINARY(11111111));
db1.resize(5);
assert(db1.to_ulong() == BOOST_BINARY(11111));
db1.clear();
assert(db1.empty() && db1.size() == 0);
// 3. push_back
// dynamic_bitset可以像vector那样使用push_back()向容器末尾(二制数头部)追加一个值
dynamic_bitset<> db5(5, BOOST_BINARY(01010));
assert(db5.to_ulong() == BOOST_BINARY(01010));
db5.push_back(true); // 添加二进制位1
assert(db5.to_ulong() == BOOST_BINARY(101010));
db5.push_back(false); // 添加二进制位0
assert(db5.to_ulong() == BOOST_BINARY(0101010));
// 4. block
// dynamic_bitset使用block来存储二进制位, 一个block就可以存储32个二进制位
assert(dynamic_bitset<>(32).num_blocks() == 1);
assert(dynamic_bitset<>(33).num_blocks() == 2);
// 5. 位运算
dynamic_bitset<> db6(4, BOOST_BINARY(1010));
db6[0] &= 1; // 按位与运算
db6[1] ^= 1; // 按位异或运算
db6[2] |= 1; // 按位或运算
assert(db6.to_ulong() == BOOST_BINARY_UL(1100));
// 6. 访问元素
dynamic_bitset<> db7(4, BOOST_BINARY(1100));
assert(!db7.test(0) && !db7.test(1));
assert(db7.any() && !db6.none());
assert(db7.count() == 2);
assert(db7.set().to_ulong() == BOOST_BINARY(1111));
assert(db7.reset().to_ulong() == BOOST_BINARY(0000));
assert(db7.set(0, 1).to_ulong() == BOOST_BINARY(0001));
assert(db7.set(2, 1).to_ulong() == BOOST_BINARY(0101));
assert(db7.reset(0).to_ulong() == BOOST_BINARY(0100));
assert(db7.find_first() == 2);
assert(db7.find_next(2) == ULONG_MAX); // 没有找到的情况
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
boost::bind四种应用场景的例子
int f(int a, int b)
{
return a + b;
}
int g(int a, int b, int c)
{
return a + b + c;
}
// 函数指针定义
typedef int (*f_type) (int, int);
typedef int (*g_type) (int, int, int);
// 使用struct仅仅是为了方便, 不必写出public
struct demo
{
int f(int a, int b)
{
return a + b;
}
};
// 函数对象
struct sf
{
int operator()(int a, int b)
{
return a + b;
}
};
void test_bind_common()
{
std::cout << boost::bind(f, 1, 2)() << std::endl;
std::cout << boost::bind(g, 1, 2, 3)() << std::endl;
}
// 1. 普通函数应用boost::bind
void test_bind_fun1()
{
// bind(f, _1, 9)(x); // f(x, 9), 相当于bind2nd(f, 9)
// bind(f, _1, _2)(x, y); // f(x, y)
// bind(f, _2, _1)(x, y); // f(y, x)
// bind(f, _1, _1)(x, y); // f(x, x), y参数被忽略
// bind(g, _1, 8, _2)(x, y); // g(x, 8, y)
// bind(g, _3, _2, _2)(x, y, z); // g(z, y, y), x参数被忽略
int x = 1, y = 2, z = 3;
std::cout << boost::bind(f, _1, 9)(x) << std::endl;
std::cout << boost::bind(f, _1, _2)(x, y) << std::endl;
std::cout << boost::bind(f, _2, _1)(x, y) << std::endl;
std::cout << boost::bind(f, _1, _1)(x, y) << std::endl;
std::cout << boost::bind(g, _1, 8, _2)(x, y) << std::endl;
std::cout << boost::bind(g, _3, _2, _2)(x, y, z) << std::endl;
}
// 2. 成员函数应用boost::bind
void test_bind_fun2()
{
demo a, &ra = a;
demo *p = &a;
// 必须在成员函数前加上取地址操作符&, 表明这是一个成员函数指针
// 第二个参数用struct demo, struct demo * 两个类型都可以
std::cout << boost::bind(&demo::f, a, _1, 20)(10) << std::endl;
std::cout << boost::bind(&demo::f, ra, _2, _1)(10, 20) << std::endl;
std::cout << boost::bind(&demo::f, p, _1, _2)(10, 20) << std::endl;
}
// 3. 成员变量
void test_bind_val()
{
typedef std::pair<int, std::string> pair_t;
pair_t p(123, "string");
std::cout << boost::bind(&pair_t::first, p)() << std::endl;
std::cout << boost::bind(&pair_t::second, p)() << std::endl;
}
// 4. 函数对象
void test_bind_functor()
{
std::cout << boost::bind(std::plus<int>(), _1, _2)(10, 20) << std::endl;
std::cout << boost::bind(std::modulus<int>(), _1, 3)(11) << std::endl;
std::cout << std::boolalpha << boost::bind(std::greater<int>(), _1, 10)(20) << std::endl;
std::cout << boost::bind<int>(sf(), _1, _2)(11, 22) << std::endl;
}
// 可以使用ref库包装了对象的引用,可以让bind 存储对象引用的拷贝,从而降低了拷贝的代价。
void test_bind_ref()
{
// 变量
int x = 10;
// 11 + 10 + 10
std::cout << boost::bind(g, _1, boost::cref(x), boost::ref(x))(11) << std::endl;
// 一个函数对象
sf af;
std::cout << boost::bind<int>(boost::ref(af), _1, _2)(11, 22) << std::endl;
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
C++指针探讨 (四) 函数对象
函数对象不是函数指针。但是,在程序代码中,它的调用方式与函数指针一样,后面加个括号就可以了。这是入门级的随笔,说的是函数对象的定义,使用,以及与函数指针,成员函数指针的关系。
函数对象实质上是一个实现了operator()--括号操作符--的类。
例如:
{
public:
int operator ()( int a, int b)
{
return a + b;
}
};
cout << add( 3 , 2 ); // 5
函数指针版本就是:
{
return a + b;
}
typedef int ( * Add) ( int a, int b);
cout << add( 3 , 2 ); // 5
呵呵,除了定义方式不一样,使用方式可是一样的。都是:
既然函数对象与函数指针在使用方式上没什么区别,那为什么要用函数对象呢?很简单,函数对象可以携带附加数据,而指针就不行了。
下面就举个使用附加数据的例子:
{
public :
less( int num):n(num){}
bool operator ()( int value)
{
return value < n;
}
private :
int n;
};
使用的时候:
cout << isLess( 9 ) << " " << isLess( 12 ); // 输出 1 0
这个例子好象太儿戏了,换一个:
int array[SIZE] = { 50 , 30 , 9 , 7 , 20 };
// 找到小于数组array中小于10的第一个数的位置
int * pa = std::find_if(array, array + SIZE, less( 10 )); // pa 指向 9 的位置
// 找到小于数组array中小于40的第一个数的位置
int * pb = std::find_if(array, array + SIZE, less( 40 )); // pb 指向 30 的位置
这里可以看出函数对象的方便了吧?可以把附加数据保存在函数对象中,是函数对象的优势所在。
它的弱势也很明显,它虽然用起来象函数指针,但毕竟不是真正的函数指针。在使用函数指针的场合中,它就无能为力了。例如,你不能将函数对象传给qsort函数!因为它只接受函数指针。
要想让一个函数既能接受函数指针,也能接受函数对象,最方便的方法就是用模板。如:
int count_n( int * array, int size, FUNC func)
{
int count = 0 ;
for ( int i = 0 ; i < size; ++ i)
if (func(array[i]))
count ++ ;
return count;
}
这个函数可以统计数组中符合条件的数据个数,如:
int array[SIZE] = { 50 , 30 , 9 , 7 , 20 };
cout << count_n(array, SIZE, less( 10 )); // 2
用函数指针也没有问题:
{
return v < 10 ;
}
cout << count_n(array, SIZE, less10); // 2
另外,函数对象还有一个函数指针无法匹敌的用法:可以用来封装类成员函数指针!
因为函数对象可以携带附加数据,而成员函数指针缺少一个类实体(类实例)指针来调用,因此,可以把类实体指针给函数对象保存起来,就可以用于调用对应类实体成员函数了。
class memfun
{
public :
memfun( void (O:: * f)( const char * ), O * o): pFunc(f), pObj(o){}
void operator ()( const char * name)
{
(pObj ->* pFunc)(name);
}
private :
void (O:: * pFunc)( const char * );
O * pObj;
};
class A
{
public :
void doIt( const char * name)
{ cout << " Hello " << name << " ! " ;}
};
memfun < A > call( & A::doIt, & a); // 保存 a::doIt指针以便调用
call( " Kitty " ); // 输出 Hello Kitty!
大功告成了,终于可以方便保存成员函数指针,以备调用了。
不过,现实是残酷的。函数对象虽然能够保有存成员函数指针和调用信息,以备象函数指针一样被调用,但是,它的能力有限,一个函数对象定义,最多只能实现一个指定参数数目的成员函数指针。
标准库的mem_fun就是这样的一个函数对象,但是它只能支持0个和1个参数这两种成员函数指针。如 int A::func()或void A::func(int)、int A::func(double)等等,要想再多一个参数如:int A::func(int, double),不好意思,不支持。想要的话,只有我们自已写了。
而且,就算是我们自已写,能写多少个?5个?10个?还是100个(这也太恐怖了)?
好在boost库提供了boost::function类,它默认支持10个参数,最多能支持50个函数参数(多了,一般来说这够用了。但它的实现就是很恐怖的:用模板部份特化及宏定义,弄了几十个模板参数,偏特化(编译期)了几十个函数对象。
----
C++0x已经被接受的一个提案,就是可变模板参数列表。用了这个技术,就不需要偏特化无数个函数对象了,只要一个函数对象模板就可以解决问题了。期待吧。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
转载请注明原文网址:
http://www.cnblogs.com/xianyunhe/archive/2011/11/26/2264709.html
函数指针是通过指向函数的指针间接调用函数。函数指针可以实现对参数类型、参数顺序、返回值都相同的函数进行封装,是多态的一种实现方式。由于类的非静态成员函数中有一个隐形的this指针,因此,类的成员函数的指针和一般函数的指针的表现形式不一样。
1、指向一般函数的指针
函数指针的声明中就包括了函数的参数类型、顺序和返回值,只能把相匹配的函数地址赋值给函数指针。为了封装同类型的函数,可以把函数指针作为通用接口函数的参数,并通过函数指针来间接调用所封装的函数。
下面是一个指向函数的指针使用的例子。
#include <iostream.h> /*指向函数的指针*/ typedef int (*pFun)(int, int); int Max(int a, int b) { return a > b ? a : b; } int Min(int a, int b) { return a < b ? a : b; } /*通用接口函数,实现对其他函数的封装*/ int Result(pFun fun, int a, int b) { return (*fun)(a, b); } void main() { int a = 3; int b = 4; cout<<"Test function pointer: "<<endl; cout<<"The maximum number between a and b is "<<Result(Max, a, b)<<endl; cout<<"The minimum number between a and b is "<<Result(Min, a, b)<<endl; }
2、指向类的成员函数的指针
类的静态成员函数采用与一般函数指针相同的调用方式,而受this指针的影响,类的非静态成员函数与一般函数指针是不兼容的。而且,不同类的this指针是不一样的,因此,指向不同类的非静态成员函数的指针也是不兼容的。指向类的非静态成员函数的指针,在声明时就需要添加类名。
下面是一个指向类的成员函数的指针的使用的例子,包括指向静态和非静态成员函数的指针的使用。
#include <iostream.h> class CA; /*指向类的非静态成员函数的指针*/ typedef int (CA::*pClassFun)(int, int); /*指向一般函数的指针*/ typedef int (*pGeneralFun)(int, int); class CA { public: int Max(int a, int b) { return a > b ? a : b; } int Min(int a, int b) { return a < b ? a : b; } static int Sum(int a, int b) { return a + b; } /*类内部的接口函数,实现对类的非静态成员函数的封装*/ int Result(pClassFun fun, int a, int b) { return (this->*fun)(a, b); } }; /*类外部的接口函数,实现对类的非静态成员函数的封装*/ int Result(CA* pA, pClassFun fun, int a, int b) { return (pA->*fun)(a, b); } /*类外部的接口函数,实现对类的静态成员函数的封装*/ int GeneralResult(pGeneralFun fun, int a, int b) { return (*fun)(a, b); } void main() { CA ca; int a = 3; int b = 4; cout<<"Test nonstatic member function pointer from member function:"<<endl; cout<<"The maximum number between a and b is "<<ca.Result(CA::Max, a, b)<<endl; cout<<"The minimum number between a and b is "<<ca.Result(CA::Min, a, b)<<endl; cout<<endl; cout<<"Test nonstatic member function pointer from external function:"<<endl; cout<<"The maximum number between a and b is "<<Result(&ca, CA::Max, a, b)<<endl; cout<<"The minimum number between a and b is "<<Result(&ca, CA::Min, a, b)<<endl; cout<<endl; cout<<"Test static member function pointer: "<<endl; cout<<"The sum of a and b is "<<GeneralResult(CA::Sum, a, b)<<endl; }+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++boost库中sleep方法详解
boost库中sleep有两个方法
1,这个方法只能在线程中用,在主线程中用无用
void sleep(TimeDuration const& rel_time);
void sleep(system_time const& abs_time);
实例
boost::this_thread::sleep(boost::posix_time::seconds(2)); // 这种更好用
boost::this_thread::sleep(boost::get_system_time() + boost::posix_time::seconds(2));
2,在主线程中使用
原型:
- sleep(const system_time& xt);
boost::thread::sleep(boost::get_system_time() + boost::posix_time::seconds(5));
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
boost::thread库的使用
boost::thread::sleep(boost::get_system_time() + boost::posix_time::seconds(5));
[1]在C++中至少使用过一种多线程开发库,有Mutex和Lock的概念。
[2]熟悉C++开发,在开发工具中,能够编译、设置boost::thread库。
通过实例介绍boost thread的使用方式,本文主要由线程启动、Interruption机制(中断)、线程同步、等待线程退出、Thread Group几个部份组成。
正文
线程启动
线程可以从以下四种方式启动:
第一种用struct结构的operator成员函数启动:
struct callable
{
void operator()() { 这里略去若干行代码 }
};
这里略去若干行代码
Callable x;
Boost::thread t(x);
第二种以非成员函数形式启动线程
void func(int nP)
{ 这里略去若干行代码
}
这里略去若干行代码
Boost::thread t(func,123);
第三种以成员函数形式启动线程
#include <boost/bind.hpp>
这里略去若干行代码
class testBind{
public:
void testFunc(int i)
{
cout<<”i=”<<i<<endl;
}
};
这里略去若干行代码
testBind tb;
boost::thread t(boost::bind(&testBind::testFunc,&tb,100));
第四种以Lambda表达方式启动
[cpp] view plain copy
boost::thread t([](int nVal)
{
cout << nVal << " from thread" << endl;
},1000);
t.join();
Interruption机制
可以通过thread对象的interrupt函数,通知线程,需要interrupt。线程运行到interruption point就可以退出。
Interruption机制举例:
#include "stdafx.h"
#include <iostream>
#include <boost/thread.hpp>
using namespace std;
void f()
{
for(int i=1;i<0x0fffffff;i++)
{
if(i%0xffffff==0)
{
cout<<"i="<<((i&0x0f000000)>>24)<<endl;
cout<<"boost::this_thread::interruption_requested()="<<boost::this_thread::interruption_requested()<<endl;
if(((i&0x0f000000)>>24)==5)
{
boost::this_thread::interruption_point();
}
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
boost::thread t(f);
t.interrupt();
t.join(); //等待线程结束
return 0;
}
t.interrupt();告诉t线程,现在需要interrupt。boost::this_thread::interruption_requested()可以得到当前线程是否有一个interrupt请求。若有interrupt请求,线程在运行至interruption点时会结束。boost::this_thread::interruption_point();就是一个interruption point。Interruption point有多种形式,较常用的有boost::this_thread::sleep(boost::posix_time::seconds(5));当没有interrupt请求时,这条语句会让当前线程sleep五秒,若有interrupt requirement线程结束。
如何使线程在运行到interruption point的时候,不会结束,可以参考下面的例子:
#include "stdafx.h"
#include <iostream>
#include <boost/thread.hpp>
using namespace std;
void f()
{
for(int i=1;i<0x0fffffff;i++)
{
if(i%0xffffff==0)
{
cout<<"i="<<((i&0x0f000000)>>24)<<endl;
cout<<"boost::this_thread::interruption_requested()"<<boost::this_thread::interruption_requested()<<endl;
if(((i&0x0f000000)>>24)==5)
{
boost::this_thread::disable_interruption di;
{
boost::this_thread::interruption_point();
}
}
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
boost::thread t(f);
t.interrupt();
t.join(); //等待线程结束
return 0;
}
注意boost::this_thread::disable_interruption这条语句的使用,它可以使大括号内的interruption point不会中断当前线程。
线程同步
Boost提供了多种lock导致上手需要较长时间,还是看下面线程同步的例子比较简单,相信在多数应用中足够:
直接使用boost::mutex的例子
static boost::mutex g_m;
这里略去若干行代码
g_m.lock();
需要锁定的代码
g_m.unlock();
这里略去若干行代码
if(g_m.try_lock())
{
需要锁定的代码
}
这里略去若干行代码
使用lock guard的例子
#include <iostream>
#include <string>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
using namespace std;
static boost::mutex g_m;
void f(string strName)
{
for(int i=1;i<0x0fffffff;i++)
{
if(i%0xffffff==0)
{
boost::lock_guard<boost::mutex> lock(g_m);
cout<<"Name="<<strName<<" i="<<((i&0x0f000000)>>24)<<endl;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
boost::thread t(f,string("inuyasha"));
boost::thread t2(f,string("kagula"));
boost::thread t3(f,string("kikyou"));
{
boost::lock_guard<boost::mutex> lock(g_m);
cout<<"thread id="<<t.get_id()<<endl;
}
t.join();
t2.join();
t3.join();
return 0;
}
使用unique lock的例子
#include <iostream>
#include <string>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
using namespace std;
static boost::mutex g_m;
void f(string strName)
{
cout<<"Thread name is "<<strName<<"-----------------begin"<<endl;
for(int i=1;i<0x0fffffff;i++)
{
if(i%0xffffff==0)
{
boost::unique_lock<boost::mutex> lock(g_m);
cout<<"Name="<<strName<<" i="<<((i&0x0f000000)>>24)<<endl;
lock.unlock();
}
}
cout<<"Thread name is "<<strName<<"-----------------end"<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
boost::thread t(f,string("inuyasha"));
boost::thread t2(f,string("kagula"));
boost::thread t3(f,string("kikyou"));
t.join();
t2.join();
t3.join();
return 0;
}
同Lock_guard相比
[1]Unique lock中有owns lock成员函数,可判断,当前有没有被lock。
[2]在构造Unique Lock时可以指定boost::defer_lock_t参数推迟锁定,直到Unique Lock实例调用Lock。或采用下面的编码方式使用:
boost::unique_lock<boost::mutex> lock(mut,boost::defer_lock);
boost::unique_lock<boost::mutex> lock2(mut2,boost::defer_lock);
boost::lock(lock,lock2);
[3]它可以和Conditoin_variable配合使用。
[4]提供了try lock功能。
如果线程之间执行顺序上有依赖关系,直接到boost官网中参考条件变量(Condition variables)的使用。官网关于Conditon Variables的说明还是容易看懂的。
注意,使用一个不恰当的同步可能消耗掉1/2以上的cpu运算能力。
Thread Group
线程组使用示例,其中f函数在上面的例子已经定义
int _tmain(int argc, _TCHAR* argv[])
{
boost::thread_group tg;
tg.add_thread(new boost::thread(f,string("inuyasha")));
tg.add_thread(new boost::thread(f,string("kagula")));
tg.add_thread(new boost::thread(f,string("kikyou")));
tg.join_all();
return 0;
}
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
std::lock_guard
lock_guard 类是一个mutex封装者,它为了拥有一个或多个mutex而提供了一种方便的 RAII style 机制。(译注:所谓的RAII,全称为Resource Acquisition Is Initialization,汉语是“资源获取即初始化”。但是这个直译并没有很好地解释这个词组的含义。其实含义并不高深复杂,简单说来就是,在资源获取的时候将其封装在某类的object中,利用"栈资源会在相应object的生命周期结束时自动销毁"来自动释放资源,即,将资源释放写在析构函数中。所以这个RAII其实就是和智能指针的实现是类似的。)
当一个lock_guard对象被创建后,它就会尝试去获得给到它的mutex的所有权。当控制权不在该lock_guard对象所被创建的那个范围后,该lock_guard就会被析构,从而mutex被释放。
如果给定几个mutex,那么死锁避免算法就会被使用,比如 std::lock. (自从C++17)
lock_guard类是 non-copyable的。
示例程序:
- #include <thread>
- #include <mutex>
- #include <iostream>
- int g_i = 0;
- std::mutex g_i_mutex; // protects g_i
- void safe_increment()
- {
- std::lock_guard<std::mutex> lock(g_i_mutex);
- ++g_i;
- std::cout << std::this_thread::get_id() << ": " << g_i << '\n';
- // g_i_mutex is automatically released when lock goes out of scope
- }
- int main()
- {
- std::cout << __func__ << ": " << g_i << '\n';
- std::thread t1(safe_increment);
- std::thread t2(safe_increment);
- t1.join();
- t2.join();
- std::cout << __func__ << ": " << g_i << '\n';
- }
可能的输出:
- main: 0
- 140641306900224: 1
- 140641298507520: 2
- main: 2
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
unique lock
1 回顾采用RAII手法管理mutex的std::lock_guard其功能是在对象构造时将mutex加锁,析构时对mutex解锁,这样一个栈对象保证了在异常情形下mutex可以在lock_guard对象析构被解锁,lock_guard拥有mutex的所有权。
- explicit lock_guard (mutex_type& m);//必须要传递一个mutex作为构造参数
- lock_guard (mutex_type& m, adopt_lock_t tag);//tag=adopt_lock表示mutex已经在之前被上锁,这里lock_guard将拥有mutex的所有权
- lock_guard (const lock_guard&) = delete;//不允许copy constructor
2 再来看一个与std::lock_guard功能相似但功能更加灵活的管理mutex的对象 std::unique_lock,unique_lock内部持有mutex的状态:locked,unlocked。unique_lock比lock_guard占用空间和速度慢一些,因为其要维护mutex的状态。
- 1 unique_lock() noexcept; //可以构造一个空的unique_lock对象,此时并不拥有任何mutex
- 2 explicit unique_lock (mutex_type& m);//拥有mutex,并调用mutex.lock()对其上锁
- 3 unique_lock (mutex_type& m, try_to_lock_t tag);//tag=try_lock表示调用mutex.try_lock()尝试加锁
- 4 unique_lock (mutex_type& m, defer_lock_t tag) noexcept;//tag=defer_lock表示不对mutex加锁,只管理mutex,此时mutex应该是没有加锁的
- 5 unique_lock (mutex_type& m, adopt_lock_t tag);//tag=adopt_lock表示mutex在此之前已经被上锁,此时unique_locl管理mutex
- 6 template <class Rep, class Period>
- unique_lock (mutex_type& m, const chrono::duration<Rep,Period>& rel_time);//在一段时间rel_time内尝试对mutex加锁,mutex.try_lock_for(rel_time)
- 7 template <class Clock, class Duration>
- unique_lock (mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);//mutex.try_lock_until(abs_time)直到abs_time尝试加锁
- 8 unique_lock (const unique_lock&) = delete;//禁止拷贝构造
- 9 unique_lock (unique_lock&& x);//获得x管理的mutex,此后x不再和mutex相关,x此后相当于一个默认构造的unique_lock,移动构造函数,具备移动语义,movable but not copyable
说明:其中2和5拥有mutex的所有权,而1和4永远不用有mutex的所有权,3和6及7若尝试加锁成功则拥有mutex的所有权
unique_lock 不一定要拥有 mutex,所以可以透过 default constructor 建立出一个空的 unique_lock。
unique_lock 虽然一样不可复制(non-copyable),但是它是可以转移的(movable)。所以,unique_lock 不但可以被函数回传,也可以放到 STL 的 container 里。
另外,unique_lock 也有提供 lock()、unlock() 等函数,可以用来加锁解锁mutex,也算是功能比较完整的地方。
unique_lock本身还可以用于std::lock参数,因为其具备lock、unlock、try_lock成员函数,这些函数不仅完成针对mutex的操作还要更新mutex的状态。
3 std::unique_lock其它成员函数
- ~unique_lock();//若unique_lock对象拥有管理的mutex的所有权,mutex没有被销毁或者unlock,那么将执行mutex::unlock()解锁,并不销毁mutex对象。
- mutex_type* mutex() const noexcept;//返回unique_lock管理的mutex指针,但是unique_lock不会放弃对mutex的管理,若unique_lock对mutex上锁了,其有义务对mutex解锁
- bool owns_lock() const noexcept;//当mutex被unique_lock上锁,且mutex没有解锁或析构,返回真,否则返回false
- explicit operator bool() const noexcept;//同上
4 std::unique_lock增加了灵活性,比如可以对mutex的管理从一个scope通过move语义转到另一个scope,不像lock_guard只能在一个scope中生存。同时也增加了管理的难度,因此如无必要还是用lock_guard。
5 网上看见一个unique_lock的应用于银行转账的实例,贴在这里:
- #include <mutex>
- #include <thread>
- #include <chrono>
- #include <iostream>
- #include <string>
- using namespace std;
- struct bank_account//银行账户
- {
- explicit bank_account(string name, int money)
- {
- sName = name;
- iMoney = money;
- }
- string sName;
- int iMoney;
- mutex mMutex;//账户都有一个锁mutex
- };
- void transfer( bank_account &from, bank_account &to, int amount )//这里缺少一个from==to的条件判断个人觉得
- {
- unique_lock<mutex> lock1( from.mMutex, defer_lock );//defer_lock表示延迟加锁,此处只管理mutex
- unique_lock<mutex> lock2( to.mMutex, defer_lock );
- lock( lock1, lock2 );//lock一次性锁住多个mutex防止deadlock
- from.iMoney -= amount;
- to.iMoney += amount;
- cout << "Transfer " << amount << " from "<< from.sName << " to " << to.sName << endl;
- }
- int main()
- {
- bank_account Account1( "User1", 100 );
- bank_account Account2( "User2", 50 );
- thread t1( [&](){ transfer( Account1, Account2, 10 ); } );//lambda表达式
- thread t2( [&](){ transfer( Account2, Account1, 5 ); } );
- t1.join();
- t2.join();
- }
说明:加锁的时候为什么不是如下这样的?在前面一篇博文中有讲到多个语句加锁可能导致deadlock,假设:同一时刻A向B转账,B也向A转账,那么先持有自己的锁再相互请求对方的锁必然deadlock。
- lock_guard<mutex> lock1( from.mMutex );
- lock_guard<mutex> lock2( to.mMutex );
- lock( from.mMutex, to.mMutex );
- lock_guard<mutex> lock1( from.mMutex, adopt_lock );//adopt_lock表示mutex已经上锁,lock1将拥有from.mMutex
- lock_guard<mutex> lock2( to.mMutex, adopt_lock );
- #include<mutex>
- #include<exception>
- #include<iostream>
- using namespace std;
- int main(){
- mutex one,two;
- try{
- {
- lock(one,two);
- throw 1;
- cout<<"locking..."<<endl;
- }
- }catch(int){
- cout<<"catch..."<<endl;
- }
- if(!one.try_lock()&&!two.try_lock())
- cout<<"failure"<<endl;
- else
- cout<<"success"<<endl;
- return 0;
- }
程序输出:
catch...
success //lock后的操作抛出异常后,mutex解锁了
7 unique_lock is movable but not copyable.因此可以作为函数返回值,STL容器元素。例如:一个函数采用unique_lock加锁mutex然后准备好数据并将unique_lock返回给调用者,调用者在mutex保护下对数据进一步加工,简单的代码如下:
- #include<mutex>
- #include<iostream>
- using namespace std;
- mutex m;
- unique_lock<mutex> get_lock(){
- unique_lock<mutex> lk(m);
- cout<<"prepare data..."<<endl;//准备数据
- return lk;//移动构造
- }
- int main(){
- unique_lock<mutex> lk(get_lock());
- cout<<"process data..."<<endl;//在mutex保护下数据深加工
- return 0;
- }
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
1. 概述
线程就是,在同一程序同一时间内允许执行不同函数的离散处理队列。 这使得一个长时间去进行某种特殊运算的函数在执行时不阻碍其他的函数变得十分重要。 线程实际上允许同时执行两种函数,而这两个函数不必相互等待。一旦一个应用程序启动,它仅包含一个默认线程。 此线程执行main() 函数。 在main()中被调用的函数则按这个线程的上下文顺序地执行。 这样的程序称为单线程程序。
反之,那些创建新的线程的程序就是多线程程序。 他们不仅可以在同一时间执行多个函数,而且这在如今多核盛行的时代显得尤为重要。 既然多核允许同时执行多个函数,这就使得对开发人员相应地使用这种处理能力提出了要求。 然而线程一直被用来当并发地执行多个函数,开发人员现在不得不仔细地构建应用来支持这种并发。 多线程编程知识也因此在多核系统时代变得越来越重要。
2. 线程管理
2.1 情景1(test_thread_wait1)
在这个库最重要的一个类就是boost::thread,它是在boost/thread.hpp里定义的,用来创建一个新线程。下面的示例来说明如何运用它。新建线程里执行的那个函数的名称被传递到boost::thread的构造函数。 一旦上述示例中的变量t 被创建,该 thread() 函数就在其所在线程中被立即执行。 同时在test_thread_wait1()里也并发地执行该 threadfun1() 。
为了防止程序终止,就需要对新建线程调用join() 方法。join() 方法是一个阻塞调用:它可以暂停当前线程,直到调用 join() 的线程运行结束。这就使得test_thread_wait1()函数一直会等待到 threadfun1()运行结束。
正如在上面的例子中看到,一个特定的线程可以通过诸如t的变量访问,通过这个变量等待着它的使用 join() 方法终止。 但是,即使 t 越界或者析构了,该线程也将继续执行。 一个线程总是在一开始就绑定到一个类型为 boost::thread 的变量,但是一旦创建,就不在取决于它。 甚至还存在着一个叫detach()的方法,允许类型为boost::thread 的变量从它对应的线程里分离。 当然了,像join()的方法之后也就不能被调用,因为这个变量不再是一个有效的线程。
任何一个函数内可以做的事情也可以在一个线程内完成。 归根结底,一个线程只不过是一个函数,除了它是同时执行的。 在上述例子中,使用一个循环把5个数字写入标准输出流。 为了减缓输出,每一个循环中调用wait() 函数让执行延迟了一秒。 wait() 可以调用一个名为sleep() 的函数,这个函数也来自于 Boost.Thread,位于 boost::this_thread 名空间内。
sleep() 要么在预计的一段时间或一个特定的时间点后时才让线程继续执行。 通过传递一个类型为boost::posix_time::seconds 的对象,在这个例子里我们指定了一段时间。 boost::posix_time::seconds 来自于Boost.DateTime库,它被 Boost.Thread 用来管理和处理时间的数据。
虽然前面的例子说明了如何等待一个不同的线程,但下面的例子演示了如何通过所谓的中断点让一个线程中断。
2.2 情景2(test_thread_wait2())
在一个线程对象上调用 interrupt() 会中断相应的线程。在这方面,中断意味着一个类型为boost::thread_interrupted的异常,它会在这个线程中抛出。然后这只有在线程达到中断点时才会发生。如果给定的线程不包含任何中断点,简单调用interrupt() 就不会起作用。每当一个线程中断点,它就会检查interrupt() 是否被调用过。只有被调用过了, boost::thread_interrupted 异常才会相应地抛出。
Boost.Thread定义了一系列的中断点,例如sleep()函数。由于sleep()在这个例子里被调用了五次,该线程就检查了五次它是否应该被中断。然而sleep()之间的调用,却不能使线程中断。
一旦该程序被执行,它只会打印三个数字到标准输出流。这是由于在test_thread_wait2()里3秒后调用 interrupt()方法。因此,相应的线程被中断,并抛出一个boost::thread_interrupted 异常。 这个异常在线程内也被正确地捕获,catch处理虽然是空的。由于thread() 函数在处理程序后返回,线程也被终止。这反过来也将终止整个程序,因为test_thread_wait2()等待该线程使用join()终止该线程。
Boost.Thread定义包括上述 sleep()函数十个中断。有了这些中断点,线程可以很容易及时中断。然而,他们并不总是最佳的选择,因为中断点必须事前读入以检查boost::thread_interrupted异常。
3.示例
- void wait(int seconds)
- {
- boost::this_thread::sleep(boost::posix_time::seconds(seconds));
- }
- void threadfun1()
- {
- for (int i = 0; i < 5; ++i)
- {
- wait(1);
- PRINT_DEBUG(i);
- }
- }
- void threadfun2()
- {
- try
- {
- for (int i = 0; i < 5; ++i)
- {
- wait(1);
- PRINT_DEBUG(i);
- }
- }
- catch (boost::thread_interrupted&)
- {
- PRINT_DEBUG("thread_interrupted");
- }
- }
- void test_thread_wait1()
- {
- boost::thread t(&threadfun1);
- // join()方法是一个阻塞调用:它可以暂停当前线程,直到调用join()的线程运行结束。
- t.join();
- }
- void test_thread_wait2()
- {
- boost::thread t(&threadfun2);
- wait(3);
- t.interrupt();
- t.join();
- }
- void test_thread_wait3()
- {
- boost::thread t(&threadfun2);
- // timed_join()方法同样也是一个阻塞调用:它可以暂停当前线程,
- // 直到调用join()的线程运行结束或者超时
- t.timed_join(boost::posix_time::seconds(3));
- }
- void test_thread_wait4()
- {
- boost::thread t(&threadfun2);
- wait(3);
- // 当thread 与线程执行体分离时,线程执行体将不受影响地继续执行,
- // 直到运行结束,或者随主线程一起结束。
- t.detach();
- // 此时join无作用
- t.join();
- // t不再标识任何线程 {Not-any-thread}
- assert(t.get_id() == boost::thread::id());
- }
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
原文如下:
http://en.cppreference.com/w/cpp/thread/unique_lock
http://en.cppreference.com/w/cpp/thread/unique_lock/unique_lock
http://en.cppreference.com/w/cpp/thread/unique_lock/~unique_lock
http://en.cppreference.com/w/cpp/thread/unique_lock/operator%3D
http://en.cppreference.com/w/cpp/thread/unique_lock/lock
http://en.cppreference.com/w/cpp/thread/unique_lock/try_lock
http://en.cppreference.com/w/cpp/thread/unique_lock/try_lock_for
http://en.cppreference.com/w/cpp/thread/unique_lock/try_lock_until
http://en.cppreference.com/w/cpp/thread/unique_lock/unlock
http://en.cppreference.com/w/cpp/thread/unique_lock/mutex
http://en.cppreference.com/w/cpp/thread/unique_lock/owns_lock
http://en.cppreference.com/w/cpp/thread/unique_lock/operator_bool
std::unique_lock
定义在头文件 中。
template <class Mutex> (since C++11)
class unique_lock;
类 unique_lock 是一个一般性质的 mutex 属主的封装,提供延迟锁定(deferred locking),限时尝试(time-constrained attempts),递归锁定(recursive locking), 锁主的转换, 以及对条件变量的使用。
类 unique_lock 是 movable 的,但不是 copyable 的 – 它满足 MoveConstructible 和 MoveAssignable, 但不满足 CopyConstructible 和 CopyAssignable.
类 unique_lock 满足 BasicLockable 的要求。如果 Mutex 满足 Lockable 的要求,那么 unique_lock 也满足 Lockable 的要求(比如,可以被用于 std::lock);如果 Mutex 满足 TimedLockable 的要求,unique_lock 也满足 TimedLockable 的要求。
模板参数
Mutex - 用于锁定的 mutex 的类型。该类型必须满足 BasicLockable 的要求。
构造函数
(1) unique_lock();
(2) unique_lock( unique_lock&& other );
(3) explicit unique_lock( mutex_type& m );
(4) unique_lock( mutex_type& m, std::defer_lock_t t );
(5) unique_lock( mutex_type& m, std::try_to_lock_t t );
(6) unique_lock( mutex_type& m, std::adopt_lock_t t );
(7) template< class Rep, class Period >
unique_lock(mutex_type& m,
const std::chrono::duration<Rep,Period>& timeout_duration);
(8) template< class Clock, class Duration >
unique_lock(mutex_type& m,
const std::chrono::time_point<Clock,Duration>& timeout_time);
(1) unique_lock();
构造一个没有关联 mutex 的 unique_lock
(2) unique_lock( unique_lock&& other );
Move构造函数,使用 other 的内容来构造 unique_lock. 使得other变成没有mutex关联的unique_lock.
(3) - (8) 构造一个以 m 为关联的mutex的unique_lock, 另外:
(3) explicit unique_lock( mutex_type& m );
通过调用 m.lock() 来锁定相关联的 mutex. 如果当前线程已经拥有了mutex,且不是递归的mutex,那么行为未定义。
(4) unique_lock( mutex_type& m, std::defer_lock_t t );
不锁定相关的mutex.
(5) unique_lock( mutex_type& m, std::try_to_lock_t t );
通过调用 m.try_lock() 来尝试锁定相关的mutex而不会阻塞。如果当前线程已经拥有mutex且不是递归mutex,则行为未定义。
(6) unique_lock( mutex_type& m, std::adopt_lock_t t );
假设线程已经拥有m.
(7)
template< class Rep, class Period >
unique_lock(mutex_type& m, const std::chrono::duration<Rep,Period>& timeout_duration);
- 1
- 2
通过调用 m.try_lock_for(timeout_duration) 试图锁定相关联的 mutex. 一直阻塞直到超时或锁定成功。也可能阻塞得比time_duration的时间更长一些。
(8)
template< class Clock, class Duration >
unique_lock(mutex_type& m, const std::chrono::time_point<Clock,Duration>& timeout_time);
- 1
- 2
通过调用 m.try_lock_until(timeout_time) 来试图锁定相关联的 mutex. 一直阻塞直到指定的时间点到达或者锁定成功。可能会在指定的时间到达后仍阻塞一会儿。
示例程序:
#include <cassert>
#include <iostream> // std::cout
#include <thread>
#include <vector>
#include <mutex>
class Number;
std::ostream& operator<<(std::ostream& stream, const Number& number);
class Number {
public:
Number() : v_(1) {}
// thread-safe update of 'a' and 'b'
static void update(Number& a, Number& b, bool order) {
// do not lock 'mutex_' of 'a' and 'b' sequentially,
// two sequential lock may lead to deadlock,
// that's why 'std::lock' exists (see below)
GuardLock lock_a(a.mutex_, std::defer_lock);
GuardLock lock_b(b.mutex_, std::defer_lock);
// mutexes is not locked
assert(!lock_a.owns_lock());
assert(!lock_b.owns_lock());
// unspecified series of calls...
std::lock(lock_a, lock_b);
// Result: 'a.mutex_' and 'b.mutex_' is in locked state
// 'a' and 'b' can be modified safety
assert(lock_a.owns_lock());
assert(lock_b.owns_lock());
if (order) {
a.v_ += b.v_;
b.v_ += a.v_;
std::cout << a << b;
}
else {
b.v_ += a.v_;
a.v_ += b.v_;
std::cout << b << a;
}
// 'lock_a' and 'lock_b' will be destroyed,
// unlocking 'a.mutex_' and 'b.mutex_'
}
// not thread-safe; used before thread creation or in thread-safe 'update'
std::ostream& print(std::ostream& stream) const {
stream << v_ << " ";
return stream;
}
private:
using Mutex = std::mutex;
using GuardLock = std::unique_lock<Mutex>;
Mutex mutex_;
int v_;
};
// not thread-safe; see 'Number::print'
std::ostream& operator<<(std::ostream& stream, const Number& number) {
return number.print(stream);
}
int main() {
Number a, b;
std::cout << a << b;
std::vector<std::thread> threads;
for (unsigned i = 0; i < 4; ++i) {
// without 'std::lock' deadlock may occur in this situation:
// thread #1 lock 'a.mutex_'
// thread #2 lock 'b.mutex_'
// thread #1 try to lock 'b.mutex_' and blocked (it's locked by #2)
// thread #2 try to lock 'a.mutex_' and blocked (it's locked by #1)
// ... deadlock
threads.emplace_back(Number::update, std::ref(a), std::ref(b), true); // #1
threads.emplace_back(Number::update, std::ref(b), std::ref(a), false); // #2
}
for (auto& i: threads) {
i.join();
}
std::cout << '\n';
}
// Output:
// 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
析构函数
销毁这个unique_lock对象。如果 *this 此时拥有一个相关联的 mutex 并且已经获得它,那么就会解锁该mutex.
赋值操作符 operator =
unique_lock& operator=( unique_lock&& other ); (since C++11)
Move赋值操作符。使用 other 中的内容来赋值给自己。
如果在此调用前,*this 已经拥有一个mutex并锁定了它,那么此调用会解锁该mutex.
std::unique_lock::lock 函数
void lock(); (since C++11)
锁定关联的mutex. 高效地调用 mutex()->lock().
异常:
- 抛出mutex()->lock()所抛出的异常。
- 如果没有相关联的 mutex, std::system_error就会抛出,所携带的错误码是std::errc::operation_not_permitted.
- 如果mutex已经被本unique_lock锁定了(换句话说,owns_lock为true),那么std::system_error就会被抛出,错误码是std::errc::resource_deadlock_would_occur.
参见示例程序:
#include <mutex>
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
int main()
{
int counter = 0;
std::mutex counter_mutex;
std::vector<std::thread> threads;
auto worker_task = [&](int id) {
// Note, this is just lock! See the document of the constructor.
std::unique_lock<std::mutex> lock(counter_mutex);
++counter;
std::cout << id << ", initial counter: " << counter << '\n';
lock.unlock();
// don't hold the lock while we simulate an expensive operation
std::this_thread::sleep_for(std::chrono::seconds(1));
lock.lock();
++counter;
std::cout << id << ", final counter: " << counter << '\n';
};
for (int i = 0; i < 10; ++i) {
// vector::push_back() cannot work due to std::thread is not copyable.
threads.emplace_back(worker_task, i);
}
for (auto &thread : threads) thread.join();
}
/**
Possible Output:
0, initial counter: 1
1, initial counter: 2
2, initial counter: 3
3, initial counter: 4
4, initial counter: 5
5, initial counter: 6
6, initial counter: 7
7, initial counter: 8
8, initial counter: 9
9, initial counter: 10
1, final counter: 11
0, final counter: 12
3, final counter: 13
5, final counter: 14
2, final counter: 15
4, final counter: 16
7, final counter: 17
9, final counter: 18
6, final counter: 19
8, final counter: 20
**/
std::unique_lock::try_lock 函数
bool try_lock(); (since C++11)
试图锁定相关联的mutex,而不会阻塞。高效地调用 mutex()->try_lock().
如果没有相关联的mutex或者该mutex已经被该unique_lock锁定,那么 std::system_error 就会被抛出。
异常:
- 任何 mutex()->try_lock() 抛出的异常都会被抛出 (Mutex类型不会被try_lock抛出,但一个自定义的Lockable类型可能会被抛出)。
- 如果没有相关联的mutex,那么std::system_error就会被抛出,其错误码是std::errc::operation_not_permitted.
- 如果mutex已经被本unique_lock锁定了,std::system_error也会被抛出,错误码是std::errc::resource_deadlock_would_occur.
std::unique_lock::try_lock_for 函数
template
std::unique_lock::unlock 函数
void unlock(); (since C++11)
解锁相关的mutex.
如果没有相关联的mutex或该mutex已经被锁定,则std::system_error就会被抛出。
异常:
- 任何被mutex()->unlock()抛出的异常都会被抛出。
- 如果没有相关联的mutex或者mutex并没有被锁定,则std::system_error就会被抛出,错误码是std::errc::operation_not_permitted.
std::unique_lock::mutex 函数
mutex_type* mutex() const; (since C++11)
返回一个指向所关联的mutex的指针,或者如果没有相关联的mutex的话就返回一个空指针。
std::unique_lock::owns_lock 函数
bool owns_lock() const; (since C++11)
检查 *this 是否已经锁住mutex.
是,则返回true;否则返回false.
std::unique_lock::operator bool 函数
explicit operator bool() const; (since C++11)
检查 *this 是否已锁定一个mutex. 高效地调用owns_lock().
是,则返回true;否则返回false.
示例程序
#include <mutex>
#include <thread>
#include <chrono>
#include <iostream>
struct Box {
explicit Box(int num) : num_things{num} {}
int num_things;
std::mutex m;
};
void transfer(Box &a, Box &b, int num)
{
// don't actually take the locks yet
std::unique_lock<std::mutex> lock1(a.m, std::defer_lock);
std::unique_lock<std::mutex> lock2(b.m, std::defer_lock);
// lock both unique_locks without deadlock
std::lock(lock1, lock2);
a.num_things -= num;
b.num_things += num;
// 'a.m' and 'b.m' mutexes unlocked outside of 'unique_lock'
}
int main()
{
Box acc1(100);
Box acc2(50);
std::thread t1(transfer, std::ref(acc1), std::ref(acc2), 10);
std::thread t2(transfer, std::ref(acc2), std::ref(acc1), 5);
t1.join();
t2.join();
std::cout << "acc1: " << acc1.num_things << std::endl;
std::cout << "acc2: " << acc2.num_things << std::endl;
}
/** Output:
acc1: 95
acc2: 55
**/
(译注: 原示例程序没有任何输出且变量命名比较令人费解,故略作修改。)
(完)
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
主要是考虑函数式编程(如std::bind)在使用时,是对参数直接拷贝,而不是引用。如下例子:
#include <functional> #include <iostream> void f(int& n1, int& n2, const int& n3) { std::cout << "In function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; ++n1; // increments the copy of n1 stored in the function object ++n2; // increments the main()'s n2 // ++n3; // compile error } int main() { int n1 = 1, n2 = 2, n3 = 3; std::function<void()> bound_f = std::bind(f, n1, std::ref(n2), std::cref(n3)); n1 = 10; n2 = 11; n3 = 12; std::cout << "Before function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; bound_f(); std::cout << "After function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; }
Output: Before function: 10 11 12 In function: 1 11 12 After function: 10 12 12
上述代码在执行std::bind后,在函数f()中n1的值仍然是1,n2和n3改成了修改的值。说明std::bind使用的是参数的拷贝而不是引用。具体为什么std::bind不使用引用,可能确实有一些需求,使得C++11的设计者认为默认应该采用拷贝,如果使用者有需求,加上std::ref即可。