您的当前位置:首页正文

BOOST应用初探

2022-09-08 来源:好走旅游网
BOOST应用初探

作者:冯刚 2011-8-4

1 背景

BOOST是由c++标准化委员会成员参与开发的c++标准的准官方库,他是C++标准库的发动机。下一代C++标准C++0x的标准库很多来自于boost。Boost库覆盖了广泛的领域,从数学库到智能指针,从模板元编程库到预处理器库及语法词法分析,从线程到lambda表达式,等等。所有Boost库都具有宽松的许可证,确保库可以被自由使用于商用软件。

2 功能 2.1 概况

Boost库涵盖了很广泛的领域,下面就常用的功能像智能指针,正则表达式,函数对象bind和function, 线程和线程池,时间日期,多索引容器,哈希容器,bimap,日志,内存池,模板元编程,循环缓冲区,tuple, 观察者模式signal2,网络通讯asio做一些介绍。

2.2 功能

2.2.1 智能指针 2.2.1.1 概论

smart pointers(智能指针)是存储“指向动态分配(在堆上)的对象的指

针”的对象。他们的行为很像 C++ 的内建指针,只是它们可以在适当的时候自动删除它们所指向的对象。智能指针在面对异常时有非常显著的作用,它们可以确保动态分配对象的完全析构。它们还可以用于跟踪多主人共享的动态分配对象。

在概念上,智能指针可以看作拥有它所指向的对象,并因此在对象不再需要时负责将它删除。

智能指针库提供了六个智能指针类模板: scoped_ptr简单的单一对象的唯一所有权。不可拷贝。 scoped_array简单的数组的唯一所有权。不可拷贝。 shared_ptr在多个指针间共享的对象所有权。 shared_arrayweak_ptr在多个指针间共享的数组所有权。 一个属于 shared_ptr 的对象的无所有权的观察者。 intrusive_ptr带有一个侵入式引用计数的对象的共享所有权。 这些模板被设计用来补充 std::auto_ptr 模板的不足。

2.2.1.2 智能指针shared_ptr

shared_ptr 类模板存储一个指向动态分配对象(一般是用 C++ new-expression 生成的)的指针。在最后一个 shared_ptr 所指向的对象被销毁或重置时,要保证它所指向的对象被删除。

每一个 shared_ptr 都符合 C++ 标准库的 CopyConstructible 和

Assignable 的必要条件,并因此能够用于标准库容器。因为提供了比较操作,因此 shared_ptr 可以和标准库中的关联式容器一起工作。

通常,一个 shared_ptr 不能正确地持有一个指向动态分配的数组的指针。关于那种用法请参见 shared_array。

因为在实现中使用了引用计数,shared_ptr实例的循环引用不会被回收。例如,如果 main() 持有一个指向 A 的 shared_ptr, A 又直接或间接持有一个指回 A 的 shared_ptr,A 的使用计数是 2。最初的 shared_ptr 析构后将导致一个使用计数为 1 的 A 被悬挂。使用 weak_ptr 以“打破循环”。

这个类模板被 T 参数化,T 是被指向的对象的类型。shared_ptr 和它的大多数成员函数对于 T 没什么要求,允许它是一个不完整类型,或者为 void。对 T 有附加要求的成员函数 (constructors, reset) 都明确地记录在下面。

只要 T* 能被隐式地转换到 U*,则 shared_ptr 就能被隐式地转换到

shared_ptr。特别是,shared_ptr 隐式转换到 shared_ptr,当 U 是 T 的一个可访问基类的时候,还能转换到 shared_ptr,以及转换到 shared_ptr

惯用手法

由于智能指针是线程安全的,建议在代码里只为一个对象生成单一的

shared_ptr,在多线程环境中传递参数时使用weak_ptr,这样当shared_ptr被析构,weak_ptr也会得到通知,变为无效,这样可以有效地防止野指针,同时也防止了潜在的循环引用,无法释放对象的问题。 shared_ptr spA = shared_ptr(new A); int func(weak_ptr wpA) {

If(!wpA.expired()) { } else { } }

func(spA);

//智能指针已被其他线程释放 //正常处理逻辑

shared_ptr spA = wpA.lock(); //Use spA

2.2.1.3 智能指针weak_ptr

简介

weak_ptr是Boost智能指针shared_ptr的一个重要伙伴。它允许打破循环依赖。另外,它还可以处理一个非常常见的问题—悬空指针。当销毁最后一个智能指针

shared_ptr,它会释放掉共享的资源。通过使用智能指针weak_ptr,这方面的信息会传播给所有观察该共享资源的智能指针weak_ptr,通过weak_ptr可以知道该共享资源已经释放,这意味着不会发生无意间访问无效指针的情形。这就是观察者模式的一个特例,也就是说,当销毁资源的时候,就会通知所有对资源感兴趣的weak_ptr,通过调用weak_ptr.expired(),当返回值为true时,就表示该共享资源已经释放了。

weak_ptr 类模板存储一个引向已被 shared_ptr 管理的对象的 \"weak reference\"(弱引用)。为了访问这个对象,一个 weak_ptr 可以利用 shared_ptr 的构造函数或成员函数 lock 转换为 shared_ptr。当最后一个指向对象的 shared_ptr 消失,而对象也被删除后,从一个引向已被删除对象的 weak_ptr 实例获取 shared_ptr 的企图就会失败:构造函数会抛出一个

boost::bad_weak_ptr 类型的异常,而 weak_ptr::lock 会返回一个 empty shared_ptr。

每一个 weak_ptr 都符合 C++ 标准库的 CopyConstructible 和 Assignable 的必要条件,并因此能够用于标准库容器。因为提供了比较操作,因此 weak_ptr 可以和标准库中的关联式容器一起工作。 weak_ptr 的操作绝不会抛出异常。

这个类模板被 T 参数化,T 是被指向的对象的类型。

相对于 shared_ptr,weak_ptr 提供了一个非常有限的操作子集,因为在多线程程序中访问它所存储的指针是非常危险的,甚至有时在一个单线程程序中也是不安全的(也就是说,它可能引起未定义行为)。姑且假设 weak_ptr 有一个返回 raw pointer(裸指针)的 get 成员函数,考虑下面这个无辜的代码片段: shared_ptr p(new int(5)); weak_ptr q(p);

// some time later

if(int * r = q.get()) {

// use *r }

设想在 if 之后,但是又恰恰在 r 被使用之前,另一个线程执行了语句 p.reset()。这样 r 就成了一个 dangling pointer(悬挂指针)。 解决这个问题的方案是从 q 创建一个临时的 shared_ptr: shared_ptr p(new int(5)); weak_ptr q(p);

// some time later

if(shared_ptr r = q.lock()) {

// use *r }

这样,r 就持有一个引向 q 所指向的对象的引用。即使在其它线程中执行了 p.reset(),那个对象也会继续活着,直到 r 离开作用域或者被 reset。通过获得一个指向这个对象的 shared_ptr,我们可以有效地保住它不被析构。

2.2.2 正则表达式

xpressive 是一个先进的、面向对象的、用于C++的正则表达式模板库。正则表达式可以以字符串方式编写并在运行期分析,也可以以表达式模板方式编写并在编译期分析。正则表达式可以相互引用,或者递归引用其本身,你就可以用它们来构建任意复杂的语法。它与Boost.Regex库的区别就是无需编译,全模板库。 如果你要在C++中处理文本,通常你有两个不相交的选项:正则表达式引擎或语法分析生成器。正则表达式引擎(如 Boost.Regex)更为强大和灵活;文本的模式以字符串方式表示,可以在运行期指定。但是,这意味着语法错误同样要到运行期才能被检测。另外,正则表达式不适合于高级的文本处理任务,如匹配平衡的、嵌套的标签。那些任务传统上都是由语法分析生成器(如 Spirit 语法分析器框架)来处理的。这些工具更为强大,但不够灵活。它们通常不允许你随时随意地修改你的语法规则。此外,它们不具备正则表达式的完全回溯语义,对于某些类型的模式来说,这样对作者更具有挑战性。

将这两种方法无缝地集合到一起,在C++的文本处理世界中占据了独特的优势。通过 xpressive,你可以选择更象使用 Boost.Regex 那样去使用它,将正则表达式表示为字符串。或者你也可以象使用 Spirit 那样使用它,将你的 regexes 写为C++表达式,获得一种专用于文本处理的嵌入式语言所带来的所有好处。更重要的是,你可以混用这两种方式,从而获得两者的好处,对于那些要静态绑定的正则表达式编写正则表达式语法 -- 由编译器进行硬编码和语法检查 -- 其它的则动态绑定并在运行期指定。这些正则表达式可以相互递归地引用,在以前的正则表达式不能进行模式匹配的字符串中进行模式匹配。

Xpressive 是一个只含头文件的模板库,这意味着你不需要修改你的构建脚本或链接任何独立的lib文件就可以使用它。你所要做的就是 #include

。如果你只使用静态 regexes,你可以只包含 xpressive_static.hpp 以提高编译速度。同样,如果你计划只使用动态regexes,则可以只包含 xpressive_dynamic.hpp。

表 28.1. xpressive的工具箱

Tool 工具 basic_regex<>

match_results<>, sub_match<>

regex_match()

regex_search()

regex_replace()

regex_iterator<>

regex_token_iterator<> Description 说明 含有一个已编译的正则表达式。

basic_regex<> 是xpressive之中最重要的类型。你用xpressive做的任何事情都要从创建一个类型为 basic_regex<> 的对象开始。 match_results<> 含有 regex_match() 或 regex_search() 操作的结果。它就象一个存有 sub_match<> 对象的向量。一个

sub_match<> 对象含有一个已标记的子表达式(在Perl中又称为后向引用)。基本上,它只是一个迭代器对,代表了已标记子表达式的开始和结束。

检查一个字符串是否匹配一个regex。regex_match() 要成功,必须是整个字符串从头到尾匹配regex。如果你给了

regex_match() 一个 match_results<>,那么它会将所有找到的带标记子表达式写入其中。 查找一个字符串,以发现其中匹配regex的子字符串。regex_search() 将尝试在字符串的每个位置查找匹配,从头部开始,当找到一个匹配或者整个字符串找完时停止。使用 regex_match() 时,如果你给了

regex_search() 一个 match_results<>,那么它会将所有找到的带标记子表达式写入其中。

给定一个输入字符串,一个regex和一个替代字符串,regex_replace() 通过将输入字符串中与regex相匹配的部分替换为替代字符串来构建一个新的字符串。替代字符串可以含有对带标记子表达式的引用。

一个与STL兼容的迭代器,可以很方便地找到在一个字符串中与某个regex匹配的所有地方。解引用一个 regex_iterator<> 会返回一个 match_results<>。递增一个

regex_iterator<> 可以找出下一个匹配。 类似于 regex_iterator<>,除了一点,解引用一个 regex_token_iterator<> 会返回一

Tool 工具 Description 说明 个字符串。缺省地,它返回与regex匹配的整个子字符串,不过它也可以被配置为每次返回任一个或整个带标记子表达式,或者甚至是不匹配 regex的部分字符串。

一个用于 basic_regex<> 对象的工厂。它将一个字符串\"编译\"为正则表达式。通常,你不需要直接关心 regex_compiler<>,因为 basic_regex<> 类有一个工厂方法,其内部使用了 regex_compiler<>。不过,如果你需要做一些花哨的东西,如创建一个带有不同 std::locale 的 basic_regex<> 对象,你就需要显式使用 regex_compiler<> 了。

regex_compiler<>

现在你应该对xpressive所提供的工具有了一些了解,你可以通过回答以下两个问题来找到合适的工具:

1. 你要用哪种类型的迭代器来遍历你的数据? 2. 你要对你的数据做什么操作?

弄明白你的迭代器类型

在xpressive中的多数类都是根据迭代器类型参数化的模板类。xpressive定义了一些常用的typedefs来让你可以更容易地选择合适的类型。你可以用下表基于你的迭代器类型来找到正确的类型。

下表 为 xpressive Typedefs 与迭代器类型

std::string::cchar const onst_iterator * sregex

cregex

std::wstring::wchar_t const_iterator const * wsregex

wcregex

basic_regex<> match_results<>

smatch cmatch wsmatch wcmatch

std::string::cchar const onst_iterator * sregex_compiler sregex_iterator

cregex_compiler cregex_iterator cregex_token_iterator

std::wstring::wchar_t const_iterator const * wsregex_compiler

wsregex_iterator

wsregex_token_iterator

wcregex_compiler wcregex_iterator wcregex_token_iterator

regex_compiler<> regex_iterator<>

regex_tokensregex_token_iterator<> _iterator

你要留意系统的命名习惯。这些类型经常要一起使用,所以命名习惯可以帮助你一致地使用它们。例如,如果你有一个 sregex,你就应该使用 smatch。 如果你用的不是以上四种迭代器类型之一,那么你可以直接用模板并指定你的迭代器类型。

示例

以下你将看到六个完整的示例程序。

检查整个字符串是否匹配一个regex

这是来自于\"简介\"一节中的示例。为便于查看,在此重复。

#include

#include using namespace boost::xpressive; int main() {

std::string hello( \"hello world!\" );

sregex rex = sregex::compile( \"(\\\\w+) (\\\\w+)!\" ); smatch what;

if( regex_match( hello, what, rex ) ) {

std::cout << what[0] << '\\n'; // whole match 整个匹配 std::cout << what[1] << '\\n'; // first capture 第一次捕获 std::cout << what[2] << '\\n'; // second capture 第二次捕获 }

return 0; }

程序输出如下:

hello world! hello world

检查一个字符串是否包含匹配某个regex的子串

请留意在这个例子中,我们是如何使用定制的 mark_tags 来使得匹配的模式更可读。我们可以在稍后使用 mark_tags 来对 match_results<> 进行索引访问。

#include

#include using namespace boost::xpressive; int main() {

char const *str = \"I was born on 5/30/1973 at 7am.\"; // 以名字定义一些定制的 mark_tags,比 s1, s2 等更有意义。 mark_tag day(1), month(2), year(3), delim(4); //该regex查找一个日期

cregex date = (month= repeat<1,2>(_d)) // 查找月份... >> (delim= (set= '/','-')) // 后跟一个分隔符...

>> (day= repeat<1,2>(_d)) >> delim // 和一个日期再跟一个分隔符... >> (year= repeat<1,2>(_d >> _d)); //和年份。

cmatch what;

if( regex_search( str, what, date ) ) {

std::cout << what[0] << '\\n'; // whole match 全匹配 std::cout << what[day] << '\\n'; // the day 日期 std::cout << what[month] << '\\n'; // the month 月份 std::cout << what[year] << '\\n'; // the year 年份

std::cout << what[delim] << '\\n'; // the delimiter 分隔符 } return 0; }

This program outputs the following: 程序输出如下:

5/30/1973 30 5 1973 /

替换匹配某个regex的所有子串

以下程序在一个字符串中查找日期并用伪-HTML标记它们。

#include

#include using namespace boost::xpressive; int main() {

std::string str( \"I was born on 5/30/1973 at 7am.\" ); // 基本上与上一个例子中的regex相同,但用的是动态regex

sregex date = sregex::compile( \"(\\\\d{1,2})([/-])(\\\\d{1,2})\\\\2((?:\\\\d{2}){1,2})\" ); // 和在Perl中一样,$& 是指向匹配该regex的子串的引用 std::string format( \"$&\" ); str = regex_replace( str, date, format ); std::cout << str << '\\n'; return 0; }

程序输出如下:

I was born on 5/30/1973 at 7am.

查找匹配某个regex的所有子串并每次一个地分步处理它们

以下程序在一个宽字符串中查找单词。它使用 wsregex_iterator。注意,对 wsregex_iterator 的解引用将产生一个 wsmatch 对象。

#include

#include using namespace boost::xpressive;

int main() {

std::wstring str( L\"This is his face.\" ); //查找一个完整的单词 wsregex token = +alnum;

wsregex_iterator cur( str.begin(), str.end(), token ); wsregex_iterator end; for( ; cur != end; ++cur ) {

wsmatch const &what = *cur; std::wcout << what[0] << L'\\n'; } return 0; }

程序输出如下:

This is his face

将字符串分拆为匹配某个regex的记号

以下程序在字符串中查找比赛时间,并且先显示分钟数再显示秒数。它使用 regex_token_iterator<>.

#include

#include using namespace boost::xpressive; int main() {

std::string str( \"Eric: 4:40, Karl: 3:35, Francesca: 2:32\" ); //查找比赛时间

sregex time = sregex::compile( \"(\\\\d):(\\\\d\\\\d)\" );

// 对于每个匹配,记号迭代器首先取出第一个被标记的子表达式的值,然后是第二个子表达式的值

int const subs[] = { 1, 2 };

sregex_token_iterator cur( str.begin(), str.end(), time, subs ); sregex_token_iterator end; for( ; cur != end; ++cur ) {

std::cout << *cur << '\\n'; } return 0; }

This program outputs the following: 程序输出如下:

4 40 3 35 2 32

用一个regex作为分隔符分拆字符串

以下程序接受一些带有html标记的文本,去掉其中的标记。它使用一个regex来匹配HTML标签,并用一个 regex_token_iterator<> 返回字符串中不匹配该regex的其余部分。

#include

#include using namespace boost::xpressive; int main() {

std::string str( \"Now is the time for all good men to come to the aid of their country.\" );

// find a HTML tag 查找一个HTML标签

sregex html = '<' >> optional('/') >> +_w >> '>';

// 以下的 -1 指示记号迭代器显示字符串中不匹配正则表达式的部分。 sregex_token_iterator cur( str.begin(), str.end(), html, -1 ); sregex_token_iterator end; for( ; cur != end; ++cur ) {

std::cout << '{' << *cur << '}'; }

std::cout << '\\n'; return 0; }

程序输出如下:

{Now }{is the time }{for all good men}{ to come to the aid of their}{ country.}

显示嵌套结果组成的树

以下是一个辅助类,示范了如何显示由嵌套结果组成的树:

//以缩入方式将嵌套结果输出至 std::cout struct output_nested_results {

int tabs_;

output_nested_results( int tabs = 0 ) : tabs_( tabs ) { }

template< typename BidiIterT >

void operator ()( match_results< BidiIterT > const &what ) const {

//首先进行缩入

typedef typename std::iterator_traits< BidiIterT >::value_type char_type; char_type space_ch = char_type(' ');

std::fill_n( std::ostream_iterator( std::cout ), tabs_ * 4, space_ch ); //输出匹配结果

std::cout << what[0] << '\\n'; //输出嵌套的匹配 std::for_each(

what.nested_results().begin(), what.nested_results().end(),

output_nested_results( tabs_ + 1 ) ); } };

2.2.3 Bind 目的

boost::bind 是标准函数 std::bind1st 和 std::bind2nd 的泛化。它支持任意的函数对象,函数,函数指针,和成员函数指针,它还能将任何参数绑定为一个特定的值,或者将输入的参数发送到任意的位置。bind 对函数对象没有任何要求,特别是,它不需要 result_type,first_argument_type 和 second_argument_type 这样的标准 typedefs。

int f(int a, int b) {

return a + b; }

int g(int a, int b, int c) {

return a + b + c; }

bind(f, 1, 2) 会产生一个“无参数”函数对象,它不需要参数并返回 f(1, 2)。同样,bind(g, 1, 2, 3)() 等价于 g(1, 2, 3)。

有选择性地只绑定一部分参数也是有可能的。bind(f, _1, 5)(x) 等价于 f(x, 5),这里,_1 是一个占位符参数,它的含义是“用第一个输入参数取代”。 bind 能够处理带有两个以上参数的函数,而且它的参数取代机制更为直观: bind(f, _2, _1)(x, y); // f(y, x)

bind(g, _1, 9, _1)(x); // g(x, 9, x)

bind(g, _3, _3, _3)(x, y, z); // g(z, z, z)

bind(g, _1, _1, _1)(x, y, z); // g(x, x, x)

注意,最后一个示例中,由 bind(g, _1, _1, _1) 生成的函数对象不包含对第一个参数以外的任何参数的引用,但是它仍然能使用一个以上的参数。所有多余的参数被悄悄地忽略,就像在第三个示例中,第一和第二个参数被忽略。 bind 持有的参数被返回的函数对象拷贝并内部持有。例如,在下面的代码中: int i = 5;

bind(f, i, _1);

一个 i 的值的拷贝被存储于函数对象中。boost::ref 和 boost::cref 可用于让函数对象存储一个引用而不是拷贝: int i = 5;

bind(f, ref(i), _1);

bind(f, cref(42), _1);

(和函数对象一起使用 bind)

bind 并不限于函数,它可以接受任何函数对象。通常情况下,生成的函数对象的 operator() 的返回类型必须显式指定(没有 typeof 操作符,返回类型无法推导):

struct F {

int operator()(int a, int b) { return a - b; } bool operator()(long a, long b) { return a == b; } }; F f;

int x = 104;

bind(f, _1, _1)(x); // f(x, x), i.e. zero

有些编译器遇到 bind(f, ...) 语法会发生问题。出于可移植性的原因,一种和上面的意思相同的可选的表达方式也被支持: boost::bind(boost::type(), f, _1, _1)(x);

但是要注意,这种可选语法只是作为一个 workaround 提供。它不是接口的一部分。

当函数对象暴露了一个名为 result_type 的内嵌类型时,显式返回类型可以被省略:

int x = 8;

bind(std::less(), _1, 9)(x);

// x < 9

【注意:这种省略返回类型的能力并非在所有的编译器上都可用。】

缺省情况下,bind 为提供的函数对象做出一份拷贝。boost::ref 和 boost::cref 可用于让它存储这个函数对象的引用,而非拷贝。当函数对象是不可拷贝的,拷贝代价高昂,或者包含状态时是很有用的,当然,在这种情况下,要求程序员确保这个函数对象在使用期间不能被销毁。 struct F2 {

int s;

typedef void result_type;

void operator()( int x ) { s += x; } };

F2 f2 = { 0 };

int a[] = { 1, 2, 3 };

std::for_each( a, a+3, bind( ref(f2), _1 ) );

assert( f2.s == 6 ); (和成员指针一起使用 bind)

成员函数的指针和数据成员的指针不是函数对象,因为它们不支持 operator()。为了方便起见,bind 接受成员指针作为它的第一个参数,而它的行为就像使用 boost::mem_fn 将成员指针转换成一个函数对象一样。换句话说,当 R 是 X::f 的返回类型(作为成员函数)或成员本身的类型(作为数据成员)时,表达式 bind(&X::f, args) 与

bind(mem_fn(&X::f), args) 等价。

【注意:mem_fn 创建的函数对象可以接受一个对象的指针,引用或智能指针作为它的第一个参数,更多的信息,参见 mem_fn 文档。】 示例:

struct X {

bool f(int a); }; X x;

shared_ptr p(new X);

int i = 5;

bind(&X::f, ref(x), _1)(i); bind(&X::f, &x, _1)(i); bind(&X::f, x, _1)(i); bind(&X::f, p, _1)(i);

// x.f(i) //(&x)->f(i)

// (internal copy of x).f(i) // (internal copy of p)->f(i)

最后两个示例的有趣之处在于它们生成“自包含”的函数对象。bind(&X::f, x, _1) 存储 x 的一个拷贝。bind(&X::f, p, _1) 存储 p 的一个拷贝,而且因为 p 是一个 boost::shared_ptr,这个函数对象保存一个属于它自己的 X 的实例的引用,而且当 p 离开它的作用域或者被 reset() 之后,这个引用依然保持有效。 (为函数组合使用嵌套的 binds)

传给 bind 的某些参数可以嵌套 bind 表达式自身: bind(f, bind(g, _1))(x); // f(g(x))

当函数对象被调用的时候,如果没有指定顺序,内部 bind 表达式先于外部 bind 表达式被求值,在外部 bind 表达式被求值的时候,用内部表达式的求值结果取代它们的占位符的位置。在上面的示例中,当用参数列表 (x) 调用那个函数对象的时候,bind(g, _1)(x) 首先被求值,生成 g(x),然后 bind(f, g(x))(x) 被求值,生成最终结果 f(g(x))。

bind 的这个特性可被用来执行函数组合。参见示例 bind_as_compose.cpp,示范如何用 bind 达到与 Boost.Compose 类似的功能。

注意第一个参数——被绑定函数对象——是不被求值的,即使它是一个由 bind 生成的函数对象或一个占位符参数,所以下面的示例不会如你所愿地工作: typedef void (*pf)(int);

std::vector v;

std::for_each(v.begin(), v.end(), bind(_1, 5));

你所要的效果,可以通过将一个辅助函数对象 apply 用作它的第一个参数而获得,作为一个函数对象,它可以支撑它的参数列表。为了方便起见,在 boost/bind/apply.hpp 头文件中提供了一个 apply 的实现。下面是一个前面的示例的修改版本:

typedef void (*pf)(int);

std::vector v;

std::for_each(v.begin(), v.end(), bind(apply(), _1, 5)); 尽管在缺省情况下,第一个参数是不被求值的,而所有其它参数被求值。但有时候不需要对第一个参数之后的其它参数求值,甚至当它们是内嵌 bind 子表达式的时候也不需要。这可以由另一个函数对象 protect 来帮助做到,它将类型掩饰起来,让 bind 无法对它进行识别和求值。在被调用的时候,protect 只是简单地不加更改地将参数列表转送到其它函数对象中。

头文件 boost/bind/protect.hpp 包含一个 protect 的实现。要在求值中保护一个 bind 函数对象,使用 protect(bind(f, ...))。 (重载的操作符(Boost 1.33 新增))

为了方便起见,由 bind 生成的函数对象重载了 logical not(逻辑非)操作符 ! 和关系操作符 ==, !=, <, <=, >, >=, &&, ||。

如果 logical_not 是一个持有一个参数 x 并返回 !x 的函数对象,则 !bind(f, ...) 等价于 bind( logical_not(), bind(f, ...) )。

如果 op 是一个关系或逻辑操作符,并且 relation 是一个持有两个参数 a 和 b 并返回 a op b 的函数对象,则 bind(f, ...) op x 等价于 bind( relation(), bind(f, ...), x )。

这实际上意味着你可以方便地对 bind 的结果求非:

std::remove_if( first, last, !bind( &X::visible, _1 ) ); // remove invisible objects

以及方便地将 bind 的结果和一个值进行比较:

std::find_if( first, last, bind( &X::name, _1 ) == \"Peter\" ); std::find_if( first, last, bind( &X::name, _1 ) == \"Peter\" || bind( &X::name, _1 ) == \"Paul\" ); 和一个占位符进行比较: bind( &X::name, _1 ) == _2

或者和另一个 bind 表达式进行比较:

std::sort( first, last, bind( &X::name, _1 ) < bind( &X::name, _2 ) ); // sort by name

示例

(和 Boost.Function 一起使用 bind) class button {

public:

boost::function onClick;

};

class player {

public:

void play(); void stop(); };

button playButton, stopButton; player thePlayer;

void connect() {

playButton.onClick = boost::bind(&player::play, &thePlayer); stopButton.onClick = boost::bind(&player::stop, &thePlayer); }

2.2.4 线程库 2.2.4.1 线程管理

2.2.4.1.1

创建线程

Class Param { Int value; }

Shared_ptr spParam = new shared_ptr< Param > (new Param);

Class A { Int Run(weak_ptr< Param > param); }

Shared_ptr spA = new shared_ptr (new A);

boost::shared_ptr thread(new

boost::thread(boost::bind(&A::Run ,spA, weak_ptr< Param >(spParam))));

2.2.4.1.2 等待线程退出

thread->join();

boost::system_time xt=delay(3);

bool const joined= thread->timed_join(xt);

thread ->timed_join(boost::posix_time::seconds(3)); 2.2.4.1.3

线程中断

thread ->interrupt(); 2.2.4.1.4

线程休眠

boost::this_thread::sleep(boost::posix_time::milliseconds(10)); 2.2.4.1.5

获取线程ID

thread::id get_id() const;

返回值:

如果*this 标识一个线程执行体, 返回有效线程标识( boost::thread::id。 否则返回空的

线程标识(default-constructed boost::thread::id)。

抛出: 无

thread->get_id(); 2.2.4.1.6

线程退出的处理

void exit_func(int exitcode) { }

boost::this_thread::at_thread_exit(boost::bind(exit_func,0));

2.2.4.2 线程池

 线程池的种类  先进先出队列线程池

typedef thread_pool fifo_pool;

 后进先出队列线程池

typedef thread_pool lifo_pool;

 优先级队列线程池

typedef thread_pool prio_pool;

 示例

typedef fifo_pool pool; //创建线程池

boost::threadpool::pool pool; pool.size_controller().resize(10); //调度线程池 //这样调度

boost::threadpool::future fut = boost::threadpool::schedule(pool,boost::bind(func,1)); 或者这样调度

pool.schedule(boost::bind(func,err)); 或那样调度

packaged_task pfunc(boost::bind(func,1)); Unique_future uf = pfunc.get_future(); pool.schedule(boost::move(pfunc));

packaged_task pfunc1(boost::bind(func,1)); Unique_future uf1 = pfunc.get_future(); pool.schedule(boost::move(pfunc1)); wait_for_all(uf, uf1); cout<//等待线程执行结果

fut->wait(); 或者定时等待

boost::xtime xt;

boost::xtime_get(&xt, boost::TIME_UTC);

io_ fut .timed_wait(xt); xt.sec += 30; xt.nsec += ns;

2.2.4.3 同步处理

2.2.4.3.1

互斥量

互斥量对象简化了线程间数据竟态保护和数据同步。一个线程通过一个锁定函数取得一个互斥量的所有权,通过一个对应的解锁函数释放所有权。互斥量可以是支持递归所有权的或非递归所有权的, 也可以同时属于多个线程。 Boost.Thread 库提供递归和非递归互斥量, 支持排他型的所有权和共享所有权(multiple-reader / single-writer)。

 所有权概念

Boost.Thread库支持四种基本的所有权概念: Lockable, TimedLockable,

SharedLockable 和UpgradeLockable。每种互斥量依据自己的目标实现一种或多种概

念。

 Lockable 概念

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

实现 Lockable 概念需要提供下面的成员函数:

  

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

通过 函数lock()或函数try_lock() 获取的所有权需要通过 unlock()来释放。

 TimedLockable 概念

TimedLockable concept 概念细化了Lockable concept概念,支持尝试获取所有权时超时放弃。

实现TimedLockable concept 概念的类型除了需要实现 Lockable concept概念。 还需要提供下面的成员函数:

 bool timed_lock(boost::system_time const& abs_time);

 template bool timed_lock(DurationType const& rel_time);

通过 函数timed_lock() 获取的所有权需要通过 unlock()来释放。

 bool timed_lock(boost::system_time const& abs_time)

效果:

当前线程尝试取得该互斥量所有权。 线程阻塞直到它获得所有权, 或等待超过指定的时间点。如果等待时间点已过, 函数的行为和 try_lock()一样。

返回:

如果取得所有权返回true, 否则返回false。

后置条件:

如果返回 true, 当前线程拥有该互斥量。

抛出:

如果出错抛出boost::thread_resource_error异常。

 template bool timed_lock(DurationType const& rel_time)

效果:

同 timed_lock(boost::get_system_time()+rel_time)。

 SharedLockable 概念

SharedLockable concept 细化了TimedLockable concept允许共享所有权

( shared ownership)和排他型所有权(exclusive ownership)。适用于标准的 multiple-reader / single-write模型: 通常一个线程可以获取排他型所有权, 此时其他线程不能获取到排他型所有权和共享型所有权, 相反的, 多个线程可以同时获得共享所有权。

如果一个类型要实现SharedLockable concept 概念, 除了需要满足TimedLockable concept 概念要求, 还需要提供下面的成员函数:

  

void lock_shared(); bool try_lock_shared(); bool unlock_shared();

bool timed_lock_shared(boost::system_time const& abs_time);

通过 函数 lock_shared(), try_lock_shared() 或 timed_lock_shared() 获取的所有权需要通过unlock_shared()来释放。

void lock_shared()

效果:

当前线程阻塞直到取得互斥量的共享所有权。 后置条件:

当前线程取得互斥量的共享所有权。 抛出:

如果出错抛出boost::thread_resource_error异常。

bool try_lock_shared()

效果:

当前线程尝试取得该互斥量的共享所有权,线程不阻塞。 返回:

如果取得互斥量的共享所有权返回true, 否则返回false。 后置条件:

如果返回 true, 当前线程取得互斥量的共享所有权。 抛出:

如果出错抛出boost::thread_resource_error异常。

bool timed_lock_shared(boost::system_time const& abs_time)

效果:

当前线程尝试取得该互斥量的共享所有权。 线程阻塞直到它获得所有权, 或等待超过指定的时间点。如果等待时间点已过, 函数的行为和 try_lock_shared()一样& nbsp;。 返回:

如果取得互斥量的共享所有权返回true, 否则返回false。。 后置条件:

如果返回 true, 当前线程取得互斥量的共享所有权。 抛出:

如果出错抛出boost::thread_resource_error异常。

void unlock_shared()

前置条件:

当前线程拥有互斥量的共享所有权。 效果:

释放当前线程拥有的互斥量共享所有权。 后置条件:

当前线程不在拥有该互斥量共享所有权。 抛出:

 UpgradeLockable 概念

UpgradeLockable concept 是SharedLockable concept的细化,特点在于允许所有权升级到独占状态。这是对共享所有权支持的multiple-reader / single-write模型的一个扩展:一个线程可以拥有upgradable ownership而同时其他线程拥有共享所有权 该线程可以在任何时候试图升级其所有权为独占式所有,如果此时没有其他线程拥有共享所有权,升级立即完成, 该线程就会拥有独占所有权, 这个所有权需要通过 unlock()来释放, 就像这个所有权是通过lock()取得一样。

如果该线程试图升级到独占所有时,其他线程拥有对应的共享所有权, 升级会失败,该线程阻塞直到其获得独占所有权。

UpgradeLockable concept中的所有权也可以降级: 独占式所有权可以降级为可升级所有权或者共享所有权,可升级所有权可以降级为一般共享所有权。 如果一个类型要实现 UpgradeLockable concept概念,除了需要实现 SharedLockable concept概念, 它还需要提供下面的成员函数:

    

void lock_upgrade(); bool unlock_upgrade();

void unlock_upgrade_and_lock(); void unlock_and_lock_upgrade();

void unlock_upgrade_and_lock_shared();

通过函数lock_upgrade() 取得的所有权需要通过函数 unlock_upgrade()来释放 。如果所有权类型通过函数unlock_xxx_and_lock_yyy()改变 , 所有权必须通过新所有权对应的释放函数来释放。

void lock_upgrade()

效果:

当前线程阻塞直到它获得可升级所有权。

后置条件:

当前线程取得该对象的可升级所有权。

抛出:

如果出错抛出boost::thread_resource_error异常。

void unlock_upgrade()

前置条件:

当前线程拥有该对象的可升级所有权。

效果:

释放当前线程拥有的该对象的可升级所有权。

后置条件:

当前线程不在拥有该对象的可升级所有权。

抛出:

void unlock_upgrade_and_lock()

前置条件:

当前线程拥有该对象的可升级所有权。

效果:

当前线程自动释放拥有的该对象可升级所有权,并试图取得该对象的独占所有权, 线程阻塞直到其获得独占所有权。

后置条件:

当前线程拥有该对象的独占所有权。

抛出:

void unlock_upgrade_and_lock_shared()

前置条件:

当前线程拥有该对象的可升级所有权。

效果:

当前线程自动释放拥有的该对象可升级所有权,取得共享所有权,该函数会立即返回。

后置条件:

当前线程拥有该对象的共享所有权。

抛出:

void unlock_and_lock_upgrade()

前置条件:

当前线程拥有该对象的独占所有权。 。

效果:

当前线程自动释放拥有的该对象独占级所有权,取得可升级所有权,该函数会立即返回。

后置条件:

当前线程自动拥有该对象可升级所有权。

抛出:

锁定类型(Lock Types)

lock_guard unique_lock shared_lock upgrade_lock

upgrade_to_unique_lock scoped_try_lock  lock_guard

lock_guard(Lockable & m)

lock_guard(Lockable & m,boost::adopt_lock_t) ~lock_guard()

template class lock_guard { public:

explicit lock_guard(Lockable& m_);

lock_guard(Lockable& m_,boost::adopt_lock_t);

~lock_guard(); };

#include

boost::lock_guard 非常简单: 构造函数传入一个可锁定对象,构造函数取得可锁定对象的所有权。 析构时释放所有权。 这样为可锁定对象提供了一个 RAII 风格的外观, 方便实现异常安全的锁定和解锁。 额外的, 构造函数the lock_guard(Lockable & m,boost::adopt_lock_t) 允许boost::lock_guard对象取得当前线程已拥有锁定的所有权,也就是不改变当前已拥有的所有权。

const defer_lock_t defer_lock={}; const try_to_lock_t try_to_lock={};

const adopt_lock_t adopt_lock={};

 unique_lock

#include

template class unique_lock { public:

unique_lock();

explicit unique_lock(Lockable& m_); unique_lock(Lockable& m_,adopt_lock_t); unique_lock(Lockable& m_,defer_lock_t); unique_lock(Lockable& m_,try_to_lock_t);

unique_lock(Lockable& m_,system_time const& target_time);

~unique_lock();

unique_lock(detail::thread_move_t > other); unique_lock(detail::thread_move_t > other);

operator detail::thread_move_t >(); detail::thread_move_t > move();

unique_lock& operator=(detail::thread_move_t > other); unique_lock& operator=(detail::thread_move_t > other);

void swap(unique_lock& other);

void swap(detail::thread_move_t > other);

void lock(); bool try_lock();

template

bool timed_lock(TimeDuration const& relative_time);

bool timed_lock(::boost::system_time const& absolute_time);

void unlock();

bool owns_lock() const;

operator unspecified-bool-type() const; bool operator!() const;

Lockable* mutex() const; Lockable* release(); };

boost::unique_lock比 boost::lock_guard复杂许多: 不仅是它提供RAII风格的外观, 它也允许推迟获得锁定, 直到lock() 函数显式调用, 或者支持非阻塞

方式获得锁定, 或者是支持超时锁定。 最后, 析构函数在可锁定对象被其锁定的情况下调用unlock() 函数。

boost::unique_lock特化实现了 TimedLockable concept 概念(e.g.

boost::unique_lock), 或者Lockable concept概念 (e.g. boost::unique_lock)。

如果函数mutex() 返回指向的 m 指针并且owns_lock() 返回 true,该

boost::unique_lock实例拥有一个可锁定对象的锁定状态 。如果该种实例被销毁, 析构函数会调用函数mutex()->unlock()。

boost::unique_lock的成员函数不是线程安全的。 特别的,

boost::unique_lock是针对单一线程对可锁定对象所有权建立模型的, 这样一个对象的成员函数(包含构造析构函数)必须在同一个线程内调用。基本上用于栈变量,不建议用于堆变量。

 shared_lock

#include

template class shared_lock { public:

shared_lock();

explicit shared_lock(Lockable& m_); shared_lock(Lockable& m_,adopt_lock_t); shared_lock(Lockable& m_,defer_lock_t); shared_lock(Lockable& m_,try_to_lock_t);

shared_lock(Lockable& m_,system_time const& target_time); shared_lock(detail::thread_move_t > other); shared_lock(detail::thread_move_t > other); shared_lock(detail::thread_move_t > other);

~shared_lock();

operator detail::thread_move_t >(); detail::thread_move_t > move();

shared_lock& operator=(detail::thread_move_t > other); shared_lock& operator=(detail::thread_move_t > other); shared_lock& operator=(detail::thread_move_t > other); void swap(shared_lock& other);

void lock();

bool try_lock();

bool timed_lock(boost::system_time const& target_time); void unlock();

operator unspecified-bool-type() const; bool operator!() const; bool owns_lock() const; };

boost::shared_lock 对Lockable concept建模 , 但是不仅支持可锁定对象, 还支持共享锁定获取。

和 boost::unique_lock一样,不仅提供RAII风格的外观, 它也允许推迟获得锁定, 直到lock() 函数显式调用, 或者支持非阻塞方式获得锁定, 或者是支持超时锁定。 最后, 析构函数在可锁定对象被其锁定的情况下调用unlock() 函数。 如果函数mutex() 返回指向的 m 指针并且owns_lock() 返回 true,该

boost::shared_lock实例拥有一个可锁定对象的锁定状态 。如果该种实例被销毁, 析构函数会调用函数mutex()->unlock()。

boost::shared_lock的成员函数不是线程安全的。 特别

的, boost::shared_lock是针对单一线程对可锁定对象所有权建立模型的, 这样一个对象的成员函数(包含构造析构函数)必须在同一个线程内调用。

 upgrade_lock

#include

template class upgrade_lock { public:

explicit upgrade_lock(Lockable& m_);

upgrade_lock(detail::thread_move_t > other); upgrade_lock(detail::thread_move_t > other);

~upgrade_lock();

operator detail::thread_move_t >(); detail::thread_move_t > move();

upgrade_lock& operator=(detail::thread_move_t > other); upgrade_lock& operator=(detail::thread_move_t > other);

void swap(upgrade_lock& other);

void lock(); void unlock();

operator unspecified-bool-type() const; bool operator!() const; bool owns_lock() const; };

和boost::unique_lock一样,boost::upgrade_lock对可锁定概念建模,但是不仅限于可锁定对提供的独占锁定,还支持可升级锁定。

和 boost::unique_lock一样,不仅提供RAII风格的外观, 它也允许推迟获得锁定, 直到lock() 函数显式调用, 或者支持非阻塞方式获得锁定, 或者是支持超时锁定。 最后, 析构函数在可锁定对象被其锁定的情况下调用unlock() 函数。

如果函数mutex() 返回指向的 m 指针并且owns_lock() 返回 true,该

boost::upgrade_lock实例拥有一个可锁定对象的锁定状态 。如果该种实例被销毁, 析构函数会调用函数mutex()->unlock()。

boost::upgrade_lock的成员函数不是线程安全的。 特别的, boost::upgrade_lock是针对单一线程对可锁定对象所有权建立模型的, 这样一个对象的成员函数(包含构造析构函数)必须在同一个线程内调用。

 upgrade_to_unique_lock

#include

template class upgrade_to_unique_lock { public:

explicit upgrade_to_unique_lock(upgrade_lock& m_);

~upgrade_to_unique_lock();

upgrade_to_unique_lock(detail::thread_move_t > other);

upgrade_to_unique_lock&

operator=(detail::thread_move_t > other);

void swap(upgrade_to_unique_lock& other);

operator unspecified-bool-type() const; bool operator!() const; bool owns_lock() const; };

boost::upgrade_to_unique_lock 允许临时从可升级锁定升级到独占锁定,如果传递boost::upgrade_lock对象的引用给构造函数,该对象将升级到独占所有权,当这个对象销毁时,可锁定对象恢复为可升级锁定。

 scoped_try_lock

class MutexType::scoped_try_lock { private:

MutexType::scoped_try_lock(MutexType::scoped_try_lock& other); MutexType::scoped_try_lock& operator=(MutexType::scoped_try_lock& other); public:

MutexType::scoped_try_lock();

explicit MutexType::scoped_try_lock(MutexType& m); MutexType::scoped_try_lock(MutexType& m_,adopt_lock_t); MutexType::scoped_try_lock(MutexType& m_,defer_lock_t); MutexType::scoped_try_lock(MutexType& m_,try_to_lock_t);

MutexType::scoped_try_lock(MutexType::scoped_try_lock&& other); MutexType::scoped_try_lock& operator=(MutexType::scoped_try_lock&& other);

void swap(MutexType::scoped_try_lock&& other);

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

bool owns_lock() const;

MutexType* mutex() const; MutexType* release(); bool operator!() const;

typedef unspecified-bool-type bool_type; operator bool_type() const; };

成员类型 scoped_try_lock 为已定义的不同的 MutexType 提供了一个类型定义。其所有的函数与boost::unique_lock 中的MutexType 类型的相同,除了带参数构造函数会调用 m.try_lock()而不是调用 m.lock()。 

锁定函数

lock(Lockable1,Lockable2,...) lock(begin,end)

try_lock(Lockable1,Lockable2,...) try_lock(begin,end) 

自由函数 lock(Lockable1,Lockable2,...)

template void lock(Lockable1& l1,Lockable2& l2);

template void lock(Lockable1& l1,Lockable2& l2,Lockable3& l3);

template

void lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4);

template

void lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4,Lockable5& l5);

效果:

锁定参数提供的 Lockable& nbsp;对象,并避免死锁。 该函数在多个线程并发调用锁定同一组互斥量(或其他可锁定对象)是安全的,并且不用指定锁定顺序,也不用担心死锁。如果函数在锁定对象时抛出异常,那么在函数退出前,该函数此次调用已锁定的对象也会被释放。

抛出:

对可锁定对象加锁时可能抛出的异常。

后置条件:

参数中提供的可锁定对象都被加锁。

自由函数lock(begin,end)

template

void lock(ForwardIterator begin,ForwardIterator end);

前置条件:

ForwardIterator 的value_type需要实现 Lockable concept。

效果:

锁定迭代区间的 Lockable对象,并避免死锁。 该函数在多个线程并发调用锁定同一组互斥量(或其他可锁定对象)是安全的,并且不用指定锁定顺序,也不用担心死锁。如果函数在锁定对象时抛出异常,那么在函数退出前,该函数此次调用已锁定的对象也会被释放。

抛出:

对可锁定对象加锁时可能抛出的异常。

后置条件:

迭代区间中的可锁定对象都被加锁。

自由函数 try_lock(Lockable1,Lockable2,。..)

template int try_lock(Lockable1& l1,Lockable2& l2);

template int try_lock(Lockable1& l1,Lockable2& l2,Lockable3& l3);

template int try_lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4);

template

int try_lock(Lockable1& l1,Lockable2& l2,Lockable3& l3,Lockable4& l4,Lockable5& l5);

效果:

对参数中的可锁定对象依次调用 try_lock() 函数, 如果有一个try_lock() 调用返回

false,那么已经获得的锁定就会被释放,并返回这个锁定失败对象在参数中的序号(以

0为基数)。

如果有一个 try_lock() 调用抛出异常, 那么已经获得的锁定会在函数退出前释放。

返回:

如果所有对象都成功加锁,返回-1, 否则返回第一个失败对象的序号(以0为基数)。

抛出:

调用try_lock()可能抛出的异常。

后置条件:

如果函数返回 -1,& nbsp;所有对象都被加锁,否则参数中的对象都不会加锁。

自由函数 try_lock(begin,end)

template

ForwardIterator try_lock(ForwardIterator begin,ForwardIterator end);

前置条件:

ForwardIterator 的value_type需要实现 Lockable concept。

效果:

通过函数 try_lock()锁定迭代区间的 Lockable对象, 如果有一个try_lock() 调用返回

false,那么已经获得的锁定就会被释放,并返回这个锁定失败对象对应的迭代器。

如果有一个 try_lock() 调用抛出异常, 那么已经获得的锁定会在函数退出前释放。

返回:

如果所有对象都成功加锁,返回end,否则返回第一个失败对象对应的迭代器。

抛出:

调用try_lock()可能抛出的异常。

后置条件:

如果函数返回end , 迭代区间内的对象都被加锁,否则迭代区间内的对象都不会加锁。

 互斥量的种类

boost::mutex实现Lockable concept ,提供一个独占式的互斥量。

对于一个实例最多允许一个线程拥有其锁定 。支持函数lock(), try_lock() 和 unlock() 并发调用。

#include

class mutex: boost::noncopyable { public: mutex(); ~mutex();

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

typedef platform-specific-type native_handle_type; native_handle_type native_handle();

typedef unique_lock scoped_lock;

typedef unspecified-type scoped_try_lock; };

 boost::timed_mutex 实现TimedLockable concept ,提供一个独占式的互斥量。

对于一个实例最多允许一个线程拥有其锁定 。支持函数lock(), try_lock() ,timed_lock()和 unlock() 并发调用。

#include

class timed_mutex: boost::noncopyable { public: timed_mutex(); ~timed_mutex();

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

bool timed_lock(system_time const & abs_time);

template

bool timed_lock(TimeDuration const & relative_time);

typedef platform-specific-type native_handle_type; native_handle_type native_handle();

typedef unique_lock scoped_timed_lock; typedef unspecified-type scoped_try_lock; typedef scoped_timed_lock scoped_lock; };

 boost::recursive_mutex;实现了 Lockable concept,提供一个递归式的互斥量 。

对于一个实例最多允许一个线程拥有其锁定 。支持函数lock(), try_lock() 和 unlock() 并发调用。如果一个线程已经锁定一个boost::recursive_mutex实例, 那么这个线程可以多次通过lock() 或

try_lock()锁定这个实例,针对每一次成功的锁定动作,需要调用unlock()来接触锁定。

#include

class recursive_mutex: boost::noncopyable { public:

recursive_mutex(); ~recursive_mutex();

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

typedef platform-specific-type native_handle_type; native_handle_type native_handle();

typedef unique_lock scoped_lock; typedef unspecified-type scoped_try_lock; };

boost::recursive_timed_mutex 实现TimedLockable concept ,提供一个递归式的

互斥量。

对于一个实例最多允许一个线程拥有其锁定 。支持函数lock(), try_lock() ,timed_lock()和 unlock() 并发调用。如果一个线程已经锁定一个boost::recursive_timed_mutex实例,那么这个线程可以多次通过lock() ,timed_lock()或 try_lock()锁定这个实例,针对每一次成功的锁定动作,需要调用unlock()来接触锁定。

#include

class recursive_timed_mutex: boost::noncopyable { public:

recursive_timed_mutex(); ~recursive_timed_mutex();

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

bool timed_lock(system_time const & abs_time);

template

bool timed_lock(TimeDuration const & relative_time);

typedef platform-specific-type native_handle_type; native_handle_type native_handle();

typedef unique_lock scoped_lock; typedef unspecified-type scoped_try_lock; typedef scoped_lock scoped_timed_lock; };

 boost::shared_mutex提供了一个multiple-reader / single-writer互斥量,实现了

UpgradeLockable concept.。

允许并发调用函数 lock(), try_lock(), timed_lock(), lock_shared(), try_lock_shared() 和 timed_lock_shared()。

#include

class shared_mutex { public:

shared_mutex(); ~shared_mutex();

void lock_shared(); bool try_lock_shared();

bool timed_lock_shared(system_time const& timeout); void unlock_shared();

void lock(); bool try_lock();

bool timed_lock(system_time const& timeout); void unlock();

void lock_upgrade(); void unlock_upgrade();

void unlock_upgrade_and_lock(); void unlock_and_lock_upgrade(); void unlock_and_lock_shared();

void unlock_upgrade_and_lock_shared(); };

2.2.4.3.2  概述

Condition 变量

类型 condition_variable 和 condition_variable_any 提供一个种机制,一个线程可以等待另外一个线程内某个事件发生的通知。 通常的应用模式是一个线程锁定一个互斥量,然后通过函数wait等待一个condition_variable 或 condition_variable_any实例,当线程从wait 函数激活时 , 检查特定的条件是否满足,如果满足,线程继续执行;如果不满足,线程继续等待。最简单的情况下,这个条件只是一个布尔变量。

boost::condition_variable cond; boost::mutex mut; bool data_ready;

void process_data();

void wait_for_data_to_process() {

boost::unique_lock lock(mut); while(!data_ready) {

cond.wait(lock); }

process_data(); }

请注意 lock 对象会传递给 wait函数: wait 函数会自动将线程添加到等待条件变量的线程集合中,并且解锁传递进来的互斥量。当线程被唤醒,函数退出前互斥量会再次被锁定。这样允许其他线程获取互斥量来更新共享的数据,确保条件变量关联的数据被正确同步。

同时,另外的线程将这个条件置为true,and 然后对条件变量调用函数

notify_one 或 notify_all来唤醒等待该条件变量的一个线程或多个线程 。

void retrieve_data(); void prepare_data();

void prepare_data_for_processing() {

retrieve_data(); prepare_data(); {

boost::lock_guard lock(mut); data_ready=true; }

cond.notify_one(); }

注意,同一个互斥量在共享数据被更新前被锁定,但是这个互斥量锁定范围不需要包含函数notify_one. 调用。

这个例子使用一个condition_variable 对象,如果换成condition_variable_any对象也是同样适用。

condition_variable_any 更加通用,支持和其他类型的互斥量和锁定类型,而condition_variable 要求传递给函数wait的是

boost::unique_lock对象。这样在已知互斥量的条件下,

condition_variable 可以做适当的优化; condition_variable_any 类型的实现比 condition_variable复杂许多。  condition_variable

#include

namespace boost {

class condition_variable { public:

condition_variable(); ~condition_variable();

void notify_one(); void notify_all();

void wait(boost::unique_lock& lock);

template

void wait(boost::unique_lock& lock,predicate_type predicate);

bool timed_wait(boost::unique_lock& lock,boost::system_time const& abs_time);

template

bool timed_wait(boost::unique_lock& lock,duration_type const& rel_time);

template

bool timed_wait(boost::unique_lock& lock,boost::system_time const& abs_time,predicate_type predicate);

template

bool timed_wait(boost::unique_lock& lock,duration_type const& rel_time,predicate_type predicate);

// backwards compatibility

bool timed_wait(boost::unique_lock& lock,boost::xtime const& abs_time);

template

bool timed_wait(boost::unique_lock& lock,boost::xtime const& abs_time,predicate_type predicate); }; }

condition_variable()

效果:

创建一个条件变量对象。

抛出:

如果出现错误,抛出异常boost::thread_resource_error。

~condition_variable()

前置条件:

所有等待这个条件变量的线程得到通知(通过调用函数notify_one 或notify_all, 尽管不是每个独立的函数调用wait或 timed_wait 都返回)。

效果:

销毁条件变量。

抛出:

无。

void notify_one()

效果:

如果有线程当前因为等待这个条件变量(wait 或timed_wait)阻塞,这个函数调用会解除一个线程的阻塞状态。

抛出:

无。

void notify_all()

效果:

如果有线程当前因为等待这个条件变量(wait 或timed_wait)阻塞,这个函数调用会解除所有这些线程的阻塞状态。

抛出:

无。

void wait(boost::unique_lock& lock)

前置条件:

lock对象被当前线程锁定, 或者被等待该状态变量的其他线程锁定,或者所有在等待该状态变量的线程传递的lock对象指向相同的互斥量。

效果:

自动调用 lock.unlock()并且阻塞当前线程。 线程在函数notify_one()或notify_all调用通知后解除阻塞状态,然后线程在 wait函数退出前调用lock.lock() 来加锁。如果这个函数因为异常退出,退出前也会调用lock.lock()。

后置条件:

当前线程锁定lock。

抛出:

如果出错抛出异常boost::thread_resource_error。如果对线程执行体关联的boost::thread对象调用函数interrupt(), 抛出boost::thread_interrupted异常。

template void wait(boost::unique_lock& lock, predicate_type pred)

效果:

while(!pred()) {

wait(lock); }

bool timed_wait(boost::unique_lock& lock,boost::system_time const& abs_time)

前置条件:

lock对象被当前线程锁定, 或者被等待该状态变量的其他线程锁定,或者所有在等待该状态变量的线程传递的lock对象指向相同的互斥量。

效果:

自动调用 lock.unlock()并且阻塞当前线程。 线程在函数notify_one()或notify_all调用通知后解除阻塞状态,另外在boost::get_system_time()得到的时间超过abs_time 后线

程也会解除阻塞, 然后线程在 wait函数退出前调用lock.lock() 来加锁。如果这个函

数因为异常退出,退出前也会调用lock.lock()。

返回:

如果函数因为指定的时间到达而退出,返回false,否则 true。

后置条件:

当前线程锁定lock。

如果出错抛出异常boost::thread_resource_error。如果对线程执行体关联的boost::thread对象调用函数interrupt(), 抛出boost::thread_interrupted异常。

template bool timed_wait(boost::unique_lock&lock,duration_type const& rel_time)

前置条件:

lock对象被当前线程锁定, 或者被等待该状态变量的其他线程锁定,或者所有在等待该状态变量的线程传递的lock对象指向相同的互斥量。

效果:

自动调用 lock.unlock()并且阻塞当前线程。 线程在函数notify_one()或notify_all调用通知后解除阻塞状态,另外在等待时间段超过rel_time 后线程也会解除阻塞, 然后线

程在 wait函数退出前调用lock.lock() 来加锁。如果这个函数因为异常退出,退出前也会调用lock.lock()。

返回:

如果函数因为指定的时间段到达而退出,返回false,否则 true。

后置条件:

当前线程锁定lock。

抛出:

如果出错抛出异常boost::thread_resource_error。如果对线程执行体关联的boost::thread对象调用函数interrupt(), 抛出boost::thread_interrupted异常。

template bool timed_wait(boost::unique_lock& lock, boost::system_time const& abs_time, predicate_type pred)

效果:

while(!pred()) {

if(!timed_wait(lock,abs_time)) {

return pred(); }

抛出:

}

return true;

 condition_variable_any

#include

namespace boost {

class condition_variable_any { public:

condition_variable_any(); ~condition_variable_any();

void notify_one(); void notify_all();

template void wait(lock_type& lock);

template void wait(lock_type& lock,predicate_type predicate);

template

bool timed_wait(lock_type& lock,boost::system_time const& abs_time);

template

bool timed_wait(lock_type& lock,duration_type const& rel_time);

template

bool timed_wait(lock_type& lock,boost::system_time const& abs_time,predicate_type predicate);

template bool timed_wait(lock_type& lock,duration_type const& rel_time,predicate_type predicate);

// backwards compatibility

template

bool timed_wait(lock_type>& lock,boost::xtime const& abs_time);

template

bool timed_wait(lock_type& lock,boost::xtime const& abs_time,predicate_type predicate); }; }

condition_variable_any()

效果:

创建一个condition_variable_any对象。

抛出:

如果出错抛出boost::thread_resource_error& nbsp;异常。

~condition_variable_any()

前置条件:

所有等待这个条件变量的线程得到通知(通过调用函数notify_one 或notify_all, 尽管不是每个独立的函数调用wait或 timed_wait 都返回)。

效果:

销毁这个对象。

抛出:

无。

void notify_one()

效果:

如果有线程当前因为等待这个条件变量(wait 或timed_wait)阻塞,这个函数调用会解除一个线程的阻塞状态。

抛出:

无。

void notify_all()

效果:

如果有线程当前因为等待这个条件变量(wait 或timed_wait)阻塞,这个函数调用会解除所有这些线程的阻塞状态。

抛出:

无。

template void wait(lock_type& lock)

效果:

自动调用 lock.unlock()并且阻塞当前线程。 线程在函数notify_one()或notify_all调用通知后解除阻塞状态,然后线程在 wait函数退出前调用lock.lock() 来加锁。如果这个函数因为异常退出,退出前也会调用lock.lock()。

后置条件:

当前线程锁定lock。

抛出:

如果出错抛出异常boost::thread_resource_error。如果对线程执行体关联的boost::thread对象调用函数interrupt(), 抛出boost::thread_interrupted异常。

template void wait(lock_type& lock, predicate_type pred)

效果:

while(!pred()) {

wait(lock); }

template bool timed_wait(lock_type& lock,boost::system_time const& abs_time)

效果:

自动调用 lock.unlock()并且阻塞当前线程。 线程在函数notify_one()或notify_all调用通知后解除阻塞状态,另外在boost::get_system_time()得到的时间超过abs_time 后线

程也会解除阻塞, 然后线程在 wait函数退出前调用lock.lock() 来加锁。如果这个函数因为异常退出,退出前也会调用lock.lock()。

返回:

如果函数因为指定的时间到达而退出,返回false,否则 true。

后置条件:

当前线程锁定lock。

抛出:

如果出错抛出异常boost::thread_resource_error。如果对线程执行体关联的boost::thread对象调用函数interrupt(), 抛出boost::thread_interrupted异常。

template bool timed_wait(lock_type& lock,duration_type const& rel_time)

效果:

自动调用 lock.unlock()并且阻塞当前线程。 线程在函数notify_one()或notify_all调用通知后解除阻塞状态,另外在等待时间段超过rel_time 后线程也会解除阻塞, 然后线

程在 wait函数退出前调用lock.lock() 来加锁。如果这个函数因为异常退出,退出前也会调用lock.lock()。

返回:

如果函数因为指定的时间段到达而退出,返回false,否则 true。

后置条件:

当前线程锁定lock。

抛出:

如果出错抛出异常boost::thread_resource_error。如果对线程执行体关联的boost::thread对象调用函数interrupt(), 抛出boost::thread_interrupted异常。

template bool timed_wait(lock_type& lock, boost::system_time const& abs_time, predicate_type pred)

效果:

while(!pred()) {

if(!timed_wait(lock,abs_time)) {

return pred(); } }

return true;

2.2.4.3.3 Barriers

barrier是一个简单的概念,也被称为 rendezvous, 是多个线程间的同步点。barrier被配置为一定数量的线程使用(n),当线程到达barrier 时,他们必须等待,直到所有线程都到达这个barrier。当最后一个线程到达这个barrier,所有的线程才能继续执行,并且这个barrier对象被复位。

#include

class barrier { public:

barrier(unsigned int count); ~barrier();

bool wait(); };

2.2.4.4 线程局部存储Thread Local Storage

thread_specific_ptr

2.2.4.4.1 概述

线程本地化存储允许多个线程对象拥有特定数据的独立拷贝. 单线程程序通常使用的的静态数据和全局数据在多线程程序中会引发访问竞争,死锁,或者数据破坏 . 一个例子就是 C运行库中的 errno 变量, 该变量放C标准库中函数的错误代码. 对于支持多线程的编译器来说,为每个线程提供一个独立的errno 是常见的做法,以此避免多个线程竞争对它的访问和更新(这也是POSIX 标准要求的).

尽管编译器通常提供一些形式的语法扩展来方便标记线程本地存储(比如用于 static 或名字空间内变量声明前的 __declspec(thread) 或__thread 标记), 但是这些支持是不可移植的, 也仅限于某些用途, 比如只支持 POD 类型. 

由boost::thread_specific_ptr实现的可移植的线程本地化存储

boost::thread_specific_ptr 提供了一个线程本地化存储机制的可移植实现,在支持Boost.Thread的所有平台上适用. 每个Each instance of

boost::thread_specific_ptr指针指向一个对象 (比如 errno) where这些对象要求在不同的线程间有不同的值。当前线程对应的对象的值可以通过成员函数获取或是通过-> 操作。这些对象的初始值为NULL ,需要通过函数改变当前线程对应的值。

如果通过reset() 将boost::thread_specific_ptr 的指向改变, 那么在此之前的值会被清理, 另外, 存储的值可以通过成员函数release() 置为NULL, 该函数同时返回这个值, 这样应用程序可以有机会销毁这个值关联的对象.

线程退出时清理

当一个线程退出, boost::thread_specific_ptr实例 所关联的对象会被销毁. 通常销毁的动作通过对被指向的对象调用delete完成, 这个行为也可以通过向 boost::thread_specific_ptr 对象构造时传递一个清理函数func()来重载.此时, 所指向对象通过调用 func(p) 来销毁 .这些清理函数调用顺序不是确定的 . 如果一个清理函数将 对象关联的值设置为一个被清理过的值, 这个值会被添加到清理列表中. 当所有 boost::thread_specific_ptr 对象被置为空时,清理过程结束.

 thread_specific_ptr

#include

template class thread_specific_ptr { public:

thread_specific_ptr();

explicit thread_specific_ptr(void (*cleanup_function)(T*)); ~thread_specific_ptr();

T* get() const; T* operator->() const; T& operator*() const;

T* release();

void reset(T* new_value=0); };

thread_specific_ptr();

要求:

delete this->get() 有意义.

效果:

构造一个 thread_specific_ptr 对象,存储一个T类型的指针,指向线程本地化对象. 在函数 reset() 被调用, 或者线程退出时,默认基于delete 的清理函数会被调用来销毁线程本地的对象.

抛出:

如果出错,抛出boost::thread_resource_error异常.

explicit thread_specific_ptr(void (*cleanup_function)(T*));

要求:

cleanup_function(this->get()) 调用不抛出异常.

效果:

构造一个 thread_specific_ptr 对象,存储一个T类型的指针,指向线程本地化对象. 在函数 reset() 被调用, 或者线程退出时,基于cleanup_function的清理函数会被调用来销毁线程本地的对象. 抛出:

如果出错,抛出boost::thread_resource_error异常.

~thread_specific_ptr();

效果:

调用函数 this->reset() to 清理当前线程相关本地存储对象, 并销毁自身(*this). 抛出:

无.

T* get() const;

返回值:

当前线程相关本地存储对象的指针.

抛出:

无.

T* operator->() const;

返回值:

this->get()

抛出:

无.

T& operator*() const;

要求:

this->get() 返回非空值.

返回值:

*(this->get())

抛出:

无.

void reset(T* new_value=0);

效果:

如果If this->get()!=new_value 并且 this->get()返回非空(non-NULL), 调用对应的清理函数, delete this->get() 或者

cleanup_function(this->get()) . 将 new_value 保存为和当前线程关联

的指针

后置条件:

this->get()==new_value

抛出:

如果出错,抛出boost::thread_resource_error异常.

T* release();

效果:

返回 this->get(), 将当前线程关联的指针置空( NULL),并不清理关联对象.

后置条件:

this->get()==0

抛出:

无.

2.2.4.5 日期和时间相关

在Boost 1.35.0,Boost.Thread 库使用Boost.Date_Time库提供超时处理。 包

括 (但不限于):

   

boost::this_thread::sleep() timed_join() timed_wait() timed_lock()

对于那些接受完整时间做参数的函数,我们需要一个boost::system_time对象。 典型的, 可以通过 boost::get_system_time()得到当前时间,然后加上一个时间段得到, 比如:

boost::system_time const timeout=boost::get_system_time() + boost::posix_time::milliseconds(500);

extern bool done; extern boost::mutex m;

extern boost::condition_variable cond;

boost::unique_lock lk(m); while(!done) {

if(!cond.timed_wait(lk,timeout)) {

throw \"timed out\"; } }

对于接受时间段(TimeDuration)做参数的函数, 我们需要一个满足Boost.Date_Time Time Duration requirements条件的对象,比如:

boost::this_thread::sleep(boost::posix_time::milliseconds(25));

boost::mutex m;

if(m.timed_lock(boost::posix_time::nanoseconds(100))) {

// ... }

 system_timee 类型

#include

typedef boost::posix_time::ptime system_time;

参考Boost.Date_Time库中boost::posix_time::ptime的文档。  自由函数 get_system_time()

#include

system_time get_system_time();

返回:

当前时间。 抛出: 无。

2.2.5 Signals2 2.2.5.1 介绍

Boost.Signals2 库实现了一个可控的信号插槽(signals and slots)系统。 信号代表了具有多个目标的回调, 在类似的系统中也称为发布者(publisher)或事件(event)。 信号与某组插槽相连接,插槽即回调的接收者 (也称为事件目标,event target,或订阅者,subscriber), 它们在信号“发出”时被调用。 信号和插槽是可控的, 因为信号和插槽(或严格地说是作为插槽出现的对象)跟踪所有的连接, 并能够在信号或插槽销毁时自动断开信号/插槽的连接。 这让使用者能够在建立信号/插槽连接的同时, 不必费力地根据相关对象的生命期去管理那些连接的生命期。

当信号连接多个插槽时,插槽的返回值和信号的返回值之间是什么关系呢? 这是个问题。 Boost.Signals2 允许用户合并多个返回值,并指定合并的方式。

Signals2是原 Boost.Signals 库的一个线程安全的变体。 接口有所更改以支持线程安全,主要是在自动连接管理方面。

2.2.5.2 教程

术语表

英文 block combiner 中文 阻塞 合并器 注释 compatibility form 兼容形式 concept connect connection disconnect metafunction mutex mutex class portable syntax preferred form 概念 连接 连接 断开 元函数 互斥体 互斥类 兼容句法 可移植句法 首选形式 preferred syntax 首选句法 scoped signal slot 域内的 信号 插槽 作用域内的  Hello, World! (初级)

下例将使用信号和插槽写出“Hello, World!”。 首先,我们创建信号 sig,该信号无参数并且返回值为空。 接着,我们使用 connect 方法将 hello 函数对象连接到信号。 最后,像函数一样使用信号 sig 来调用插槽,它将转而调用 HelloWorld::operator() 打印“Hello, World!”。

struct HelloWorld {

void operator()() const {

std::cout << \"Hello, World!\" << std::endl; } };

// Signal with no arguments and a void return value boost::signals2::signal sig;

// Connect a HelloWorld slot HelloWorld hello; sig.connect(hello);

// Call all of the slots sig();  调用多个插槽

连接多个插槽(初级) 插槽调用组排序(中级)

 连接多个插槽(初级)

从信号调用单个插槽不是很有意思,因此我们将打印“Hello, World!”的工作拆分到两个完全独立的插槽,让 Hello, World 程序更有趣点。 第一个插槽将打印“Hello”,可能看起来像这样:

struct Hello {

void operator()() const {

std::cout << \"Hello\"; } };

第二个插槽将打印“, World!”和换行,以完成该程序。第二个插槽可能看起来像这样:

struct World {

void operator()() const {

std::cout << \ } };

和上个例子一样,我们创建信号 sig,它没有参数并且返回值为 void。 这次,我们将 hello 和 world 插槽都连接到同一个信号,当我们调用该信号,两个插槽都将会被调用。

boost::signals2::signal sig;

sig.connect(Hello()); sig.connect(World());

sig();

默认情况下,插槽会被加到插槽链表的尾部,因此该程序的输出应该是:

Hello, World!

 插槽调用组排序(中级)

插槽可以有副作用,这意味着某些插槽必须在另一些之前调用,即使它们不是按那个次序连接的。 Boost.Signals2 库允许插槽进行分组,并按某种方式排序编组。 对于我们的 Hello, World 程序,我们要“Hello”在“, World!”之前打印,所以我们将“Hello”放入一个组,该组将在“, World!”所在组之前执行。 为了做到这一点,我们可以在 connect 调用的头部提供一个额外的参数,以指定编组。 编组值默认为 int,并按整型的 < 关系排序。 我们这样构造 Hello, World:

boost::signals2::signal sig;

sig.connect(1, World()); // connect with group 1 sig.connect(0, Hello()); // connect with group 0

调用该信号将正确打印出“Hello, World!”,因为 Hello 对象在组 0,它在 World 对象所在的组 1 之前。 编组参数实际上是可选的。在第一个 Hello World 例子中我们省略了它,因为当所有的插槽都独立时,编组是不必要的。 那么,如果我们混合调用使用和不使用编组参数的连接会怎样? “未命名”插槽(即那些连接时未指定组名的插槽)可置于插槽链表的头部或尾部(通过向 connect 分别传入 boost::signals2::at_front 或 boost::signals2::at_back 作为最后的参数),而默认为链表的结尾。 当指定了编组时,最后的参数 at_front 或 at_back 描述的是插槽在组内的次序。 以 at_front 连接的未分组的插槽将总是在所有分组的插槽之前。以 at_back 连接的未分组的插槽将总是在所有分组的插槽之后。

如果在我们的例子中添加新的插槽,如下:

struct GoodMorning {

void operator()() const {

std::cout << \"... and good morning!\" << std::endl; } };

// 缺省的slots自动连接到slots列表的末尾 sig.connect(GoodMorning()); // slots 被调用的顺序是:

// 1) 使用boost::signals2::at_front标志连接的非编组的slots

// 2) 根据编组序号调用的编组slots // 3) 使用boost::signals2::at_ back标志连接的非编组的slots sig();

... we will get the result we wanted: ……我们会得到想要的结果:

Hello, World! ... and good morning!

 传值到插槽及回传

插槽的参数(初级) 信号的返回值(高级)

 插槽的参数(初级)

信号可以向它们调用的每个插槽传递参数。 例如,一个传递鼠标移动事件的信号可能要传入新的鼠标坐标以及是否按了鼠标键。

例如,我们创建一个信号,它将传入两个 float 参数到它的插槽。 然后我们再创建几个插槽,打印对这两个参数进行算术运算的各种结果。

void print_args(float x, float y) {

std::cout << \"The arguments are \" << x << \" and \" << y << std::endl; }

void print_sum(float x, float y) {

std::cout << \"The sum is \" << x + y << std::endl; }

void print_product(float x, float y) {

std::cout << \"The product is \" << x * y << std::endl; }

void print_difference(float x, float y) {

std::cout << \"The difference is \" << x - y << std::endl; }

void print_quotient(float x, float y) {

std::cout << \"The quotient is \" << x / y << std::endl; }

boost::signals2::signal sig;

sig.connect(&print_args); sig.connect(&print_sum);

sig.connect(&print_product); sig.connect(&print_difference); sig.connect(&print_quotient);

sig(5., 3.); 该程序将打印输出如下:

The arguments are 5 and 3 The sum is 8 The product is 15 The difference is 2 The quotient is 1.66667

当像函数一样调用 sig 时,输入它的任何值都传给了每一个插槽。 创建信号时,我们必须预先声明这些值的类型。 类型 boost::signals2::signal 表明信号具有 void 返回值并接受两个 float 值。 因此任何连接到 sig 的插槽都必须能够接受两个 float 值。  信号返回值(高级)

正如插槽可以接收参数,它们也可以返回值。 然后这些值可以通过 合并器(combiner) 返回给信号的调用者。 合并器是这样一种工具,它接收插槽调用的结果(可能没有结果,也可能有100个结果;程序运行时才知道),并且把它们合并成单一的结果返回给调用者。 该单一的结果往往是插槽调用结果的一个简单函数,可能是:最后的插槽调用的结果、所有插槽返回值的最大值,或包含所有结果的容器。

我们可以稍微修改前面的算术运算的例子,使插槽分别返回加减乘除的计算结果。 然后信号本身就可以根据这些结果返回一个值,并打印出来。

float product(float x, float y) { return x * y; } float quotient(float x, float y) { return x / y; } float sum(float x, float y) { return x + y; }

float difference(float x, float y) { return x - y; } boost::signals2::signal sig;

sig.connect(&product); sig.connect("ient);

sig.connect(&sum);

sig.connect(&difference);

// 缺省的合并器返回slots列表的最后一个slots的返回值,也就是difference 函数

std::cout << *sig(5, 3) << std::endl;

该例程将输出 2。这是因为具有返回类型(float,即输入 boost::signals2::signal 类模板的第一个模板参数)的信号的默认行为是,调用所有的插槽,然后返回最后一个被调用插槽的结果。 对本例来说,该行为确实有点傻,因为这些插槽没有副作用,所以结果就是最后连接的插槽。 求所有插槽返回值的最大值,这样的信号结果才有点意思。为此,我们创建一个自定义合并器如下:

// 合并器返回所有slots返回值中的最大值 template struct maximum {

typedef T result_type;

template

T operator()(InputIterator first, InputIterator last) const {

// If there are no slots to call, just return the // default-constructed value if(first == last ) return T(); T max_value = *first++; while (first != last) { if (max_value < *first) max_value = *first; ++first; }

return max_value; } };

maximum 类模板就像一个函数对象。 它的结果类型由其模板参数给出,并且它正是基于该类型计算最大值(例如,maximum 将在一系列 float 中查找最大的 float)。 当调用 maximum 对象时,将给出一个输入迭代器序列 [first, last),其中包含了所有插槽调用的结果。 maximum 利用该输入迭代器序列来计算最大元素,并返回那个最大值。

我们要把这个新的函数对象作为合并器安装到我们的信号,才能实际使用它。合并器模板参数跟在信号的调用签名式之后。

boost::signals2::signal > sig;

现在我们可以连接执行算术功能的插槽并使用信号了:

sig.connect(&product); sig.connect("ient); sig.connect(&sum);

sig.connect(&difference);

// Outputs the maximum value returned by the connected slots, in this case

// 15 from the product function.

std::cout << \"maximum: \" << sig(5, 3) << std::endl;

该程序的输出为 15,因为不管插槽的连接次序如何, 5 和 3 的乘积将大于商、和,或差。

在其他情况下,我们可能需要同时返回插槽计算的所有值,如保存在一个大型的数据结构中。用一个不同的合并器就可以轻松做到:

// aggregate_values is a combiner which places all the values returned

// from slots into a container template struct aggregate_values {

typedef Container result_type;

template Container operator()(InputIterator first, InputIterator last) const {

Container values;

while(first != last) {

values.push_back(*first); ++first; }

return values;

} };

我们再次用这个新的合并器创建信号:

boost::signals2::signal > > sig;

sig.connect("ient); sig.connect(&product); sig.connect(&sum);

sig.connect(&difference);

std::vector results = sig(5, 3); std::cout << \"aggregate values: \";

std::copy(results.begin(), results.end(),

std::ostream_iterator(std::cout, \" \")); std::cout << \"\\n\";

该程序的输出将包含 15、8、1.6667,和 2。有趣的是, signal 类的第一个模板参数, float,竟然不是信号的返回类型。 相反,该参数是所连接插槽的返回类型,并且它也是传入合并器的输入迭代器的 value_type。 合并器本身是个函数对象,并且它的 result_type 成员类型将成为信号的返回类型。 传给合并器的输入迭代器会将解引用操作转换为插槽调用。 因此合并器可选择仅调用某些符合特定条件的插槽。 例如,在分布计算系统中,合并器可能会询问每个远程系统能否处理请求。 对于一个特定请求,仅需一个远程系统进行处理,因此当一个远程系统接受该工作后,我们将不再要求任何其他远程系统来做同一个任务。 这样一个合并器只需检查迭代器解引用的返回值,并当该值可以接受时就返回。 以下的合并器返回第一个指向 FulfilledRequest 数据结构的非空指针,而不必要求任何以后的插槽来完成请求:

struct DistributeRequest {

typedef FulfilledRequest* result_type;

template

result_type operator()(InputIterator first, InputIterator last) const {

while (first != last) {

if (result_type fulfilled = *first) return fulfilled; ++first; }

return 0;

} };

 连接管理

断开插槽(初级) 阻塞插槽(初级) 域内连接(中级) 断开等价的插槽(中级) 自动连接管理(中级)

Postconstructor 和 Predestructor (高级) 何时断开?(中级) 传递插槽(中级)

 断开插槽(初级)

插槽在连接之后不必无限期地存在。插槽往往只是用来接收一些事件然后断开,当插槽不再需要保持连接时,程序员可以做出决定并控制。

显式管理连接的入口点是 boost::signals2::connection 类。 connection 类唯一代表了特定信号与特定插槽之间的连接。 connected() 方法检查信号与插槽是否仍保持连接, 如果信号与插槽是连接着的, disconnect() 方法断开它们的连接。 每次调用信号的 connect() 方法,就返回一个连接对象,该对象用于确定连接是否仍然存在,或者用于断开信号和插槽。

boost::signals2::connection c = sig.connect(HelloWorld()); std::cout << \"c is connected\\n\"; sig(); // Prints \"Hello, World!\"

c.disconnect(); // Disconnect the HelloWorld object std::cout << \"c is disconnected\\n\";

sig(); // Does nothing: there are no connected slots  阻塞插槽(初级)

插槽可以被临时“阻塞”,即当信号被调用时,这些插槽将被忽略,但并没有被永久地断开。这主要用于防止无限递归,如有时,要没有阻塞,插槽运行时会再次调用它所连接的信号。 boost::signals2::shared_connection_block 对象会临时阻塞插槽。 要解除阻塞,需要销毁所有引用该连接的

shared_connection_block 对象,或者调用它们的 unblock。 以下是阻塞插槽和解除的例子:

boost::signals2::connection c = sig.connect(HelloWorld()); std::cout << \"c is not blocked.\\n\"; sig(); // Prints \"Hello, World!\"

{

boost::signals2::shared_connection_block block(c); // block the slot

std::cout << \"c is blocked.\\n\";

sig(); // No output: the slot is blocked

} // shared_connection_block going out of scope unblocks the slot

std::cout << \"c is not blocked.\\n\"; sig(); // Prints \"Hello, World!\  作用域内连接(中级)

boost::signals2::scoped_connection 类引用了一个信号/插槽的连接,当 scoped_connection 类出作用域时,该连接将会被断开。 当仅需临时连接时,该功能很有用,如:

{

boost::signals2::scoped_connection c(sig.connect(ShortLived()));

sig(); // will call ShortLived function object

} // scoped_connection goes out of scope and disconnects

sig(); // ShortLived function object no longer connected to sig 注意,试图用赋值语句初始化 scoped_connection 会失败,因为它是不可拷贝的(noncopyable)。 应该使用显式初始化, 或者缺省构造然后用 connection 对它赋值:

// doesn't compile due to compiler attempting to copy a temporary scoped_connection object

// boost::signals2::scoped_connection c0 = sig.connect(ShortLived()); // okay

boost::signals2::scoped_connection c1(sig.connect(ShortLived()));

// also okay

boost::signals2::scoped_connection c2; c2 = sig.connect(ShortLived());

 断开等价的插槽(中级)

你可以使用 signal::disconnect 方法断开与给定函数对象等价的多个插槽,只要该函数对象的类型具有可访问的 == 运算符。例如:

void foo() { std::cout << \"foo\"; } void bar() { std::cout << \"bar\\n\"; } boost::signals2::signal sig;

sig.connect(&foo); sig.connect(&bar); sig();

// disconnects foo, but not bar sig.disconnect(&foo); sig();

 自动连接管理(中级)

Boost.Signals2 能自动跟踪信号/插槽连接中所涉及对象的生命期,包括当插槽调用中涉及的对象销毁时自动断开插槽。 例如,考虑一个简单的新闻发送服务,其中客户会连接到新闻提供者,而新闻提供者一有信息到达,就发送新闻到所有连接的客户。该新闻发送服务可能像这样构造:

class NewsItem { /* ... */ };

typedef boost::signals2::signal signal_type;

signal_type deliverNews;

希望接收新闻更新的客户只需连接一个函数对象,该对象可以接收传给 deliverNews 信号的新闻条目,例如:

struct NewsMessageArea : public MessageArea { public: // ...

void displayNews(const NewsItem& news) const {

messageText = news.text(); update(); } }; // ...

NewsMessageArea *newsMessageArea = new NewsMessageArea(/* ... */); // ...

deliverNews.connect(boost::bind(&NewsMessageArea::displayNews, newsMessageArea, _1));

不过,如果用户关闭新闻讯息区,销毁了 deliverNews 所知的 newsMessageArea 对象,那会怎么样? 最有可能的是产生段错误。 然而,在Boost.Signals2中,使用 slot::track,你可以跟踪由 shared_ptr 管理的任何对象, 当插槽的任一个被跟踪对象失效时,插槽会自动断开。 另外,当插槽正在执行时,

Boost.Signals2 会保证它所关联的被跟踪对象不会失效。 为此,插槽在执行前会创建被跟踪对象的临时 shared_ptr 副本。 为了跟踪 NewsMessageArea,我们用 shared_ptr 管理其生命期,并在连接它之前,通过其 slot::track 方法,把 shared_ptr 传给插槽。 例如:

// ...

boost::shared_ptr newsMessageArea(new NewsMessageArea(/* ... */)); // ...

deliverNews.connect(signal_type::slot_type(&NewsMessageArea::displayNews, newsMessageArea.get(), _1).track(newsMessageArea));

注意上例中不需要显式调用 bind()。 如果 slot 构造函数传入一个以上的参数, 它会自动将所有参数传给 bind, 并使用返回的函数对象。

还要注意插槽构造函数的第2个参数, 我们使用 newsMessageArea.get(), 传入了一个普通指针, 而不是 shared_ptr 本身。 如果传入 newsMessageArea 本身, 插槽函数将绑定一个 shared_ptr 副本, 从而妨碍了 shared_ptr 的失效(也可以是weak_ptr,这样就不会妨碍newsMessageArea的失效)。 然而,使用 slot::track 意味着我们希望允许被跟踪对象失效, 并且当它发生时自动断开连接。 其实也可以这样

deliverNews.connect(signal_type::slot_type(&NewsMessageArea::displayNews,

boost::weak_ptr< NewsMessageArea >(newsMessageArea), _1).track(newsMessageArea));

 何时断开?(中级)

以下任一条件发生时,信号/插槽将断开:

 连接被显式地断开:直接通过连接的 disconnect 方法, 或间接地通过信号的

disconnect 方法,或 scoped_connection 的析构函数。

 插槽所跟踪的对象被销毁。  信号被销毁。

这些事件可以发生于任何时间,而不会破坏信号的调用序列。 如果信号/插槽的连接在信号调用序列的任意时刻被断开,调用序列仍将继续,只是不会调用被断开的插槽。 此外,信号可以在调用序列中间被销毁,这时,它将完成其插槽调用序列,只是不可以被直接访问。

信号可以被递归调用(例如,信号A调用插槽B,而插槽B又调用信号A……)。 递归情况下,断开的行为不会改变,只是插槽调用序列包括所有嵌套的信号调用。

注意,即使在连接断开后, 其相关的插槽可能仍处于执行中。 也就是说,断开连接不会停止等待连接相关的插槽完成执行。 该情形可以发生在多线程环境下, 如断开连接与信号调用并行发生, 或者在单线程环境下,如插槽断开自身。

 传递插槽(中级)

Boost.Signals2 库中的插槽可以从任意的函数对象创建,因此没有固定的类型。 不过通常要求通过不可模板化的接口传递插槽。 对于每个特定的信号类型,都可以通过 slot_type 传递插槽, 而与信号的签名式兼容的任意函数对象,都可以传给 slot_type 参数。 例如:

// a pretend GUI button

class Button {

typedef boost::signals2::signal OnClick; public:

typedef OnClick::slot_type OnClickSlotType;

// forward slots through Button interface to its private signal

boost::signals2::connection doOnClick(const OnClickSlotType & slot);

// simulate user clicking on GUI button at coordinates 52, 38

void simulateClick(); private:

OnClick onClick; };

boost::signals2::connection Button::doOnClick(const OnClickSlotType & slot) {

return onClick.connect(slot); }

void Button::simulateClick() {

onClick(52, 38); }

void printCoordinates(long x, long y) {

std::cout << \"(\" << x << \}

Button button;

button.doOnClick(&printCoordinates); button.simulateClick();

doOnClick 方法现在功能上等效于 onClick 信号的 connect 方法,但是 doOnClick 方

法的细节可以隐藏于细节实现文件中。

 让插槽可以操作其连接(高级)

可能有这样的情形: 你希望在插槽中断开或阻塞插槽本身的连接。 例如,有一组异步任务,当每个任务完成时会发出信号。 你希望用一个插槽连接所有的任务,来接收它们完成时的结果。 一旦某个任务完成并且插槽被运行,该插槽就不必再连接到这个已经完成的任务。 因此,你可能希望让插槽运行时断开其连接,以清理旧连接。

为了让插槽断开(或阻塞)其连接, 它必须能够操作 connection 对象,即调用的信号-插槽连接对象。 难点是, connection 对象是由 signal::connect 方法返回的, 因此只有在插槽连接信号之后才能用。 在多线程环境下,这尤其麻烦, 当插槽正在被连接时,信号可能被其他线程并发调用。

因此,信号类提供了 signal::connect_extended 方法,它允许连接信号的插槽接受一个额外参数。 该额外参数是个 connection 对象,它指向当前调用插槽的信号-插槽连接。 signal::connect_extended 使用的插槽类型由 signal::extended_slot_type 定义。

例程一节中有个 extended_slot 程序,它演示了 signal::connect_extended 的用法。

namespace bs2 = boost::signals2;

void single_shot_slot(const bs2::connection &conn, const std::string &message) {

conn.disconnect(); std::cout << message; }

int main() {

typedef bs2::signal sig_type; sig_type sig; {

sig_type::extended_slot_type hello(&single_shot_slot, _1, \"Hello\");

sig.connect_extended(hello); }

sig();

// prints \"Hello\" {

sig_type::extended_slot_type world(&single_shot_slot, _1, \World!\\n\");

sig.connect_extended(world); }

sig();

// only prints \ sig();

// prints nothing, world slot has disconnected itself

return 0; };

 改变信号的互斥体类型(高级)

signal 的模板类型参数 Mutex 默认为 boost::signals2::mutex, 多数情况下,这应该能行。 如果你希望使用其他的互斥体(mutex)类型,那么它必须可缺省构造, 并且满足由 Boost.Thread 库所定义的 Lockable 概念

(concept)。 即,它必须具有 lock() 和 unlock() 方法(Lockable 概念同时包含了 try_lock() 方法, 但本库没用到)。

Boost.Signals2 库提供了另外一个可用于 signal 的互斥类:

boost::signals2::dummy_mutex。 这是个用于单线程的假互斥体,因为在单线程中,用真实的互斥体加锁是无谓的开销。 其它可用于 signal 的互斥体类型有: boost::mutex 和 C++0x 的 std::mutex。

更改信号的 Mutex 模板类型参数很烦, 因为在它之前有许多模板参数。 这时,signal_type 元函数(metafunction)就特别有用, 它开启了 signal 类的命名模板类型参数。 例如,声明一个信号,接收一个 int 参数,并使用 boost::signals2::dummy_mutex 作为其 Mutex 类型,你可以这样写:

namespace bs2 = boost::signals2; using bs2::keywords;

bs2::signal_type >::type sig;

2.2.6 Function 2.2.6.1 介绍

Boost.Function 有两种语法形式:首选形式和兼容形式。首选形式更接近于 C++ 语言,并减少了需要被考虑的独立的模板参数的个数,通常会改进可读性,但是,由于编译器的 bug 首选形式并不被所有的平台支持。兼容性是可以工作在所有被 Boost.Function 支持的编译器上。参考下面的表格以确定在你的编译器上使用哪种语法形式。

首选语法 兼容语法      

GNU C++ 2.95.x, 3.0.x, 3.1.x Comeau C++ 4.2.45.2 SGI MIPSpro 7.3.0 Intel C++ 5.0, 6.0 Compaq's cxx 6.2 Microsoft Visual C++ 7.1

    

支持首选语法的任何编译器

Microsoft Visual C++ 6.0, 7.0 Borland C++ 5.5.1

Sun WorkShop 6 update 2 C++ 5.3 Metrowerks CodeWarrior 8.1

如果你的编译器没有出现在这个列表中,请试用首选语法并向 Boost 邮件列表报告你的结果,以便我们可以保持这个表格的最新状态。 (基本用法)

一个函数包装类可以简单地通过用想要的返回类型和参数类型来实例化

function 类模板来定义,简称为 C++ 函数类型。任何个数的参数都可以提供,直到某个实现定义的上限(10 是缺省的最大值)。下面就是声明一个 function object wrapper(函数对象包装类)f,它持有两个 int 参数并返回一个 float:

首选语法 兼容语法 boost::function f; boost::function2 f;

缺省情况下,函数对象包装类是空的,所以我们创建一个函数对象赋给 f:

struct int_div {

float operator()(int x, int y) const { return ((float)x)/y; }; };

f = int_div();

现在我们可以用 f 来执行底层的函数对象 int_div:

std::cout << f(5, 3) << std::endl;

我们可以自由地赋任何函数对象给 f。如果 int_div 被定义为持有两个 long 操作数,隐式转换会在没有任何用户干预的情况下应用于参数。对于参数类型的仅有的限制是它们是可拷贝构造的,所以我们甚至可以用引用或数组:

首选语法 boost::function sum_avg;

兼容语法 boost::function4 sum_avg;

void do_sum_avg(int values[], int n, int& sum, float& avg) {

sum = 0;

for (int i = 0; i < n; i++) sum += values[i]; avg = (float)sum / n; }

sum_avg = &do_sum_avg;

调用一个实际上没有包含函数对象的函数对象包装类是一个 precondition violation(前提违例),很像试图用一个空的函数指针调用函数,并抛出一个 bad_function_call 异常。我们可以通过在一个布尔上下文中使用它(如果包装类不为空,将求值为 true)或将它和 0 作比较来检查空函数对象包装类。例如:

if (f)

std::cout << f(5, 3) << std::endl; else

std::cout << \"f has no target, so it is unsafe to call\" << std::endl;

另一个可选方法是,empty() 方法可以返回这个包装类是否为空。

最后,我们可以通过为它赋值为 0 或调用 clear() 成员函数来清空一个函数目标,例如,

f = 0;

(自由函数)

自由函数指针被认为是带有 const 函数调用操作符的单例函数对象,并因此可以直接用于函数对象包装类:

float mul_ints(int x, int y) { return ((float)x) * y; } f = &mul_ints;

注意,& 并不是真的必要,除非你使用 Microsoft Visual C++ 版本 6。 (成员函数)

在很多系统中,回调通常是对一个特定对象的成员函数的调用。这通常被认为是“参数绑定”,而且已经超出 Boost.Function 的范畴。无论如何,直接调用成员函数是被支持的,所以下面的代码是合法的:

struct X { int foo(int); };

首选语法 兼容语法 boost::function f;

f = &X::foo; X x;

f(&x, 5); boost::function2 f;

f = &X::foo; X x;

f(&x, 5);

有几个支持参数绑定的库已经存在。下面概述三个这样的库:

Bind。这个库允许任何函数对象的参数绑定。它是轻量级的而且可移植性好。

 C++ 标准库。将 std::bind1st 和 std::mem_fun 合在一起使用,可以将一个指向成员函数的指针的对象绑定到 Boost.Function:

首选语法 兼容语法 boost::function f; X x;

f = std::bind1st(

std::mem_fun(&X::foo), boost::function1 f; X x;

f = std::bind1st(

std::mem_fun(&X::foo),

首选语法 兼容语法 &x);

f(5); // Call x.foo(5)

&x);

f(5); // Call x.foo(5)

Lambda 库。这个库提供一个强大的合成机制,利用非常自然的 C++ 语法去构造函数对象。lambda 需要一个相当符合 C++ 标准的编译器。

(引向函数对象的引用)

在某些情况下,让Boost.Function 克隆一个函数对象需要付出高昂的代价(或者是语义错误的)。在这样的情况下,要求 Boost.Function 只保留引向实际的函数对象的引用是有可能的。这可以通过使用 ref 和 cref 函数去包装一个引向函数对象的引用来做到:

首选语法 兼容语法 stateful_type

a_function_object;

boost::function f; f =

boost::ref(a_function_object);

boost::function f2(f); stateful_type

a_function_object;

boost::function1 f; f =

boost::ref(a_function_object);

boost::function1 f2(f);

这里,f 不会生成 a_function_object 的一个拷贝,当 f2 以 f 的引向

a_function_object 的引用为目标时,也不会生成这个拷贝。另外,当使用引向函数对象的引用时,Boost.Function 在赋值和构造过程中不会抛出异常。 (比较 Boost.Function 函数对象)

函数对象包装类可以通过 == 或 != 与任何能够存储在这个包装类内的函数对象进行比较。如果这个函数对象包装类包含一个某种类型的函数对象,它将和给定的函数对象(这个函数对象必须是 EqualityComparable 的,而且必须有一个 boost::function_equal 的重载)进行比较。例如:

int compute_with_X(X*, int);

f = &X::foo;

assert(f == &X::foo); assert(&compute_with_X != f);

当和一个 reference_wrapper 的实例进行比较时,reference_wrapper 中的对象的地址和函数对象包装类中存储的对象的地址进行比较:

a_stateful_object so1, so2; f = boost::ref(so1);

assert(f == boost::ref(so1));

assert(f == so1); // Only if a_stateful_object is EqualityComparable assert(f != boost::ref(so2));

Copyright © 2019- 版权所有