线程

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()--括号操作符--的类。
例如:
class Add
{
public:
  int   operator ()( int  a,  int  b)
  {
    
return  a  +  b;
  }
};

Add add;  //  定义函数对象
cout  <<  add( 3 , 2 ); // 5

函数指针版本就是:
int  AddFunc( int  a,  int  b)
{
  
return  a  +  b;
}
typedef 
int  ( * Add) ( int  a,  int  b);

Add add  =   & AddFunc;
cout 
<<  add( 3 , 2 ); // 5

呵呵,除了定义方式不一样,使用方式可是一样的。都是:
cout  <<  add( 3 , 2 );

既然函数对象与函数指针在使用方式上没什么区别,那为什么要用函数对象呢?很简单,函数对象可以携带附加数据,而指针就不行了。
下面就举个使用附加数据的例子:
class  less
{
public :
    less(
int  num):n(num){}
    
bool   operator ()( int  value)
    {
        
return  value  <  n;
    }
private :
    
int  n;
};


使用的时候:
    less isLess( 10 );
    cout 
<<  isLess( 9 <<   "   "   <<  isLess( 12 );  //  输出 1 0

这个例子好象太儿戏了,换一个:
const   int  SIZE  =   5 ;
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函数!因为它只接受函数指针。

要想让一个函数既能接受函数指针,也能接受函数对象,最方便的方法就是用模板。如:
template < typename FUNC >
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;
}

这个函数可以统计数组中符合条件的数据个数,如:
const   int  SIZE  =   5 ;
int  array[SIZE]  =  {  50 30 9 7 20 };
cout 
<<  count_n(array, SIZE, less( 10 ));  //  2

用函数指针也没有问题:
bool  less10( int  v)
{
    
return  v  <   10 ;
}
cout 
<<  count_n(array, SIZE, less10);  //  2

另外,函数对象还有一个函数指针无法匹敌的用法:可以用来封装类成员函数指针!
因为函数对象可以携带附加数据,而成员函数指针缺少一个类实体(类实例)指针来调用,因此,可以把类实体指针给函数对象保存起来,就可以用于调用对应类实体成员函数了。

template < typename O >
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  <<   " ! " ;}
};


    A a;
    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,在主线程中使用

原型:

  1. 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的。

示例程序:

  1. #include <thread>  
  2. #include <mutex>  
  3. #include <iostream>  
  4.    
  5. int g_i = 0;  
  6. std::mutex g_i_mutex;  // protects g_i  
  7.    
  8. void safe_increment()  
  9. {  
  10.     std::lock_guard<std::mutex> lock(g_i_mutex);  
  11.     ++g_i;  
  12.    
  13.     std::cout << std::this_thread::get_id() << ": " << g_i << '\n';  
  14.    
  15.     // g_i_mutex is automatically released when lock goes out of scope  
  16. }  
  17.    
  18. int main()  
  19. {  
  20.     std::cout << __func__ << ": " << g_i << '\n';  
  21.    
  22.     std::thread t1(safe_increment);  
  23.     std::thread t2(safe_increment);  
  24.    
  25.     t1.join();  
  26.     t2.join();  
  27.    
  28.     std::cout << __func__ << ": " << g_i << '\n';  
  29. }  

可能的输出:

  1. main: 0  
  2. 140641306900224: 1  
  3. 140641298507520: 2  
  4. main: 2 

TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT

unique lock


1 回顾采用RAII手法管理mutex的std::lock_guard其功能是在对象构造时将mutex加锁,析构时对mutex解锁,这样一个栈对象保证了在异常情形下mutex可以在lock_guard对象析构被解锁,lock_guard拥有mutex的所有权

  1. explicit lock_guard (mutex_type& m);//必须要传递一个mutex作为构造参数  
  2. lock_guard (mutex_type& m, adopt_lock_t tag);//tag=adopt_lock表示mutex已经在之前被上锁,这里lock_guard将拥有mutex的所有权  
  3. 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. 1 unique_lock() noexcept;   //可以构造一个空的unique_lock对象,此时并不拥有任何mutex  
  2.   
  3. explicit unique_lock (mutex_type& m);//拥有mutex,并调用mutex.lock()对其上锁      
  4.   
  5. 3 unique_lock (mutex_type& m, try_to_lock_t tag);//tag=try_lock表示调用mutex.try_lock()尝试加锁  
  6.   
  7. 4 unique_lock (mutex_type& m, defer_lock_t tag) noexcept;//tag=defer_lock表示不对mutex加锁,只管理mutex,此时mutex应该是没有加锁的  
  8.   
  9. 5 unique_lock (mutex_type& m, adopt_lock_t tag);//tag=adopt_lock表示mutex在此之前已经被上锁,此时unique_locl管理mutex  
  10.   
  11. template <class Rep, class Period>  
  12.    unique_lock (mutex_type& m, const chrono::duration<Rep,Period>& rel_time);//在一段时间rel_time内尝试对mutex加锁,mutex.try_lock_for(rel_time)  
  13.   
  14. template <class Clock, class Duration>  
  15.    unique_lock (mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);//mutex.try_lock_until(abs_time)直到abs_time尝试加锁  
  16.   
  17. 8 unique_lock (const unique_lock&) = delete;//禁止拷贝构造  
  18.   
  19. 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 在使用上比lock_guard更具有弹性, 和 lock_guard 相比,unique_lock 主要的特色在于:
         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其它成员函数

  1. ~unique_lock();//若unique_lock对象拥有管理的mutex的所有权,mutex没有被销毁或者unlock,那么将执行mutex::unlock()解锁,并不销毁mutex对象。  
  2. mutex_type* mutex() const noexcept;//返回unique_lock管理的mutex指针,但是unique_lock不会放弃对mutex的管理,若unique_lock对mutex上锁了,其有义务对mutex解锁  
  3. bool owns_lock() const noexcept;//当mutex被unique_lock上锁,且mutex没有解锁或析构,返回真,否则返回false  
  4. explicit operator bool() const noexcept;//同上  


            4  std::unique_lock增加了灵活性,比如可以对mutex的管理从一个scope通过move语义转到另一个scope,不像lock_guard只能在一个scope中生存。同时也增加了管理的难度,因此如无必要还是用lock_guard。



  

            5 网上看见一个unique_lock的应用于银行转账的实例,贴在这里:

  1. #include <mutex>  
  2. #include <thread>  
  3. #include <chrono>  
  4. #include <iostream>  
  5. #include <string>  
  6. using namespace std;  
  7. struct bank_account//银行账户  
  8. {  
  9.   explicit bank_account(string name, int money)  
  10.   {  
  11.     sName = name;  
  12.     iMoney = money;  
  13.   }  
  14.   
  15.   string sName;  
  16.   int iMoney;  
  17.   mutex mMutex;//账户都有一个锁mutex  
  18. };  
  19. void transfer( bank_account &from, bank_account &to, int amount )//这里缺少一个from==to的条件判断个人觉得  
  20. {  
  21.   unique_lock<mutex> lock1( from.mMutex, defer_lock );//defer_lock表示延迟加锁,此处只管理mutex  
  22.   unique_lock<mutex> lock2( to.mMutex, defer_lock );  
  23.   lock( lock1, lock2 );//lock一次性锁住多个mutex防止deadlock  
  24.   from.iMoney -= amount;  
  25.   to.iMoney += amount;  
  26.   cout << "Transfer " << amount << " from "<< from.sName << " to " << to.sName << endl;  
  27. }  
  28. int main()  
  29. {  
  30.   bank_account Account1( "User1", 100 );  
  31.   bank_account Account2( "User2", 50 );  
  32.   thread t1( [&](){ transfer( Account1, Account2, 10 ); } );//lambda表达式  
  33.   thread t2( [&](){ transfer( Account2, Account1, 5 ); } );  
  34.   t1.join();  
  35.   t2.join();  
  36. }  


            说明:加锁的时候为什么不是如下这样的?在前面一篇博文中有讲到多个语句加锁可能导致deadlock,假设:同一时刻A向B转账,B也向A转账,那么先持有自己的锁再相互请求对方的锁必然deadlock。
  1. lock_guard<mutex> lock1( from.mMutex );  
  2. lock_guard<mutex> lock2( to.mMutex );  
              采用lock_guard也可以如下:
  1. lock( from.mMutex, to.mMutex );  
  2. lock_guard<mutex> lock1( from.mMutex, adopt_lock );//adopt_lock表示mutex已经上锁,lock1将拥有from.mMutex  
  3. lock_guard<mutex> lock2( to.mMutex, adopt_lock );  

             6 上面的例子lock针对mutex加锁后,并没有显示解锁,那么离开lock的作用域后解锁了吗?验证代码如下,在lock后抛出异常mutex解锁了吗?:
  1. #include<mutex>  
  2. #include<exception>  
  3. #include<iostream>  
  4. using namespace std;  
  5. int main(){  
  6.     mutex one,two;  
  7.     try{  
  8.         {  
  9.             lock(one,two);  
  10.             throw 1;  
  11.             cout<<"locking..."<<endl;  
  12.         }  
  13.     }catch(int){  
  14.         cout<<"catch..."<<endl;  
  15.     }  
  16.     if(!one.try_lock()&&!two.try_lock())  
  17.         cout<<"failure"<<endl;  
  18.     else  
  19.         cout<<"success"<<endl;  
  20.     return 0;  
  21. }  

程序输出:

catch...
success          //lock后的操作抛出异常后,mutex解锁了


            7 unique_lock is movable but not copyable.因此可以作为函数返回值,STL容器元素。例如:一个函数采用unique_lock加锁mutex然后准备好数据并将unique_lock返回给调用者,调用者在mutex保护下对数据进一步加工,简单的代码如下:

  1. #include<mutex>  
  2. #include<iostream>  
  3. using namespace std;  
  4. mutex m;  
  5. unique_lock<mutex> get_lock(){  
  6.     unique_lock<mutex> lk(m);  
  7.     cout<<"prepare data..."<<endl;//准备数据  
  8.     return lk;//移动构造  
  9. }  
  10. int main(){  
  11.     unique_lock<mutex> lk(get_lock());  
  12.     cout<<"process data..."<<endl;//在mutex保护下数据深加工  
  13.     return 0;  
  14. }  



            8 unique_lock::lock(), unique_lock::unlock()这一组成员函数充分说明了,unique_lock在构造时不必对mutex加锁且可以在后期某个时候对mutex加锁; unique_lock可以在自己实例销毁前调用unique_lock::unlock()提前释放锁,这对于一些分支语句中可能得到性能提升。

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.示例

  1. void wait(int seconds)  
  2. {  
  3.     boost::this_thread::sleep(boost::posix_time::seconds(seconds));  
  4. }  
  5.   
  6. void threadfun1()  
  7. {  
  8.     for (int i = 0; i < 5; ++i)  
  9.     {  
  10.         wait(1);  
  11.         PRINT_DEBUG(i);  
  12.     }  
  13. }  
  14.   
  15. void threadfun2()   
  16. {  
  17.     try  
  18.     {  
  19.         for (int i = 0; i < 5; ++i)  
  20.         {  
  21.             wait(1);  
  22.             PRINT_DEBUG(i);  
  23.         }  
  24.     }  
  25.     catch (boost::thread_interrupted&)  
  26.     {  
  27.         PRINT_DEBUG("thread_interrupted");  
  28.     }  
  29. }  
  30.   
  31. void test_thread_wait1()  
  32. {  
  33.     boost::thread t(&threadfun1);  
  34.     // join()方法是一个阻塞调用:它可以暂停当前线程,直到调用join()的线程运行结束。  
  35.     t.join();  
  36. }  
  37.   
  38. void test_thread_wait2()  
  39. {  
  40.     boost::thread t(&threadfun2);  
  41.     wait(3);  
  42.     t.interrupt();  
  43.     t.join();  
  44. }  
  45.   
  46. void test_thread_wait3()  
  47. {  
  48.     boost::thread t(&threadfun2);  
  49.     // timed_join()方法同样也是一个阻塞调用:它可以暂停当前线程,  
  50.     // 直到调用join()的线程运行结束或者超时  
  51.     t.timed_join(boost::posix_time::seconds(3));  
  52. }  
  53.   
  54. void test_thread_wait4()  
  55. {  
  56.     boost::thread t(&threadfun2);  
  57.     wait(3);  
  58.     // 当thread 与线程执行体分离时,线程执行体将不受影响地继续执行,  
  59.     // 直到运行结束,或者随主线程一起结束。  
  60.     t.detach();  
  61.     // 此时join无作用  
  62.     t.join();  
  63.     // t不再标识任何线程 {Not-any-thread}  
  64.     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即可。















































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值