置顶随笔
协程
协程,即协作式程序,其思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态。协程可以在运行期间的某个点上暂停执行,并在恢复运行时从暂停的点上继续执行。 协程已经被证明是一种非常有用的程序组件,不仅被python、lua、ruby等脚本语言广泛采用,而且被新一代面向多核的编程语言如golang rust-lang等采用作为并发的基本单位。 协程可以被认为是一种用户空间线程,与传统的线程相比,有2个主要的优点:
- 与线程不同,协程是自己主动让出CPU,并交付他期望的下一个协程运行,而不是在任何时候都有可能被系统调度打断。因此协程的使用更加清晰易懂,并且多数情况下不需要锁机制。
- 与线程相比,协程的切换由程序控制,发生在用户空间而非内核空间,因此切换的代价非常小。
网络编程模型
首先来简单回顾一下一些常用的网络编程模型。网络编程模型可以大体的分为同步模型和异步模型两类。
同步模型使用阻塞IO模式,在阻塞IO模式下调用read等IO函数时会阻塞线程直到IO完成或失败。
同步模型的典型代表是thread per connection模型,每当阻塞在主线程上的accept调用返回时则创建一个新的线程去服务于新的socket的读/写。这种模型的优点是程序简洁,编写简单;缺点是可伸缩性收到线程数的限制,当连接越来越多时,线程也越来越多,频繁的线程切换会严重拖累性能。
异步模型一般使用非阻塞IO模式,并配合epoll/select/poll等多路复用机制。在非阻塞模式下调用read,如果没有数据可读则立即返回并通知用户没有可读(EAGAIN/EWOULDBLOCK),而非阻塞当前线程。异步模型可以使一个线程同时服务于多个IO对象。
异步模型的典型代表是reactor模型。在reactor模型中,我们将所有要处理的IO事件注册到一个中心的IO多路复用器中(一般为epoll/select/poll),同时主线程阻塞在多路复用器上。一旦有IO事件到来或者就绪,多路复用器返回并将对应的IO事件分发到对应的处理器(即回调函数)中,最后处理器调用read/write函数来进行IO操作。
异步模型的特点是性能和可伸缩性比同步模型要好很多,但是其结构复杂,不易于编写和维护。在异步模型中,IO之前的代码(IO任务的提交者)和IO之后的处理代码(回调函数)是割裂开来的。
协程与网络编程
协程为克服同步模型和异步模型的缺点,并结合他们的优点提供了可能: 现在假设我们有3个协程A,B,C分别要进行数次IO操作。这3个协程运行在同一个调度器或者说线程的上下文中,并依次使用CPU。调度器在其内部维护了一个多路复用器(epoll/select/poll)。
协程A首先运行,当它执行到一个IO操作,但该IO操作并没有立即就绪时,A将该IO事件注册到调度器中,并主动放弃CPU。这时调度器将B切换到CPU上开始执行,同样,当它碰到一个IO操作的时候将IO事件注册到调度器中,并主动放弃CPU。调度器将C切换到cpu上开始执行。当所有协程都被“阻塞”后,调度器检查注册的IO事件是否发生或就绪。假设此时协程B注册的IO时间已经就绪,调度器将恢复B的执行,B将从上次放弃CPU的地方接着向下运行。A和C同理。
这样,对于每一个协程来说,是同步的模型;但是对于整个应用程序来说,却是异步的模型。
好了,原理说完了,我们来看一个实际的例子,echo server。
echo server
在这个例子中,我们将使用orchid库来编写一个echo server。orchid库是一个构建于boost基础上的 协程/网络IO 库。
echo server首先必须要处理连接事件,我们创建一个协程来专门处理连接事件:
typedef boost::shared_ptr<orchid::socket> socket_ptr;
//处理ACCEPT事件的协程
void handle_accept(orchid::coroutine_handle co) {
try {
orchid::acceptor acceptor(co -> get_scheduler().get_io_service());//构建一个acceptor
acceptor.bind_and_listen("5678",true);
for(;;) {
socket_ptr sock(new orchid::socket(co -> get_scheduler().get_io_service()));
acceptor.accept(*sock,co);
//在调度器上创建一个协程来服务新的socket。第一个参数是要创建的协程的main函数,第二个参数是要创建的协程的栈的大小。
co -> get_scheduler().spawn(boost::bind(handle_io,_1,sock),orchid::minimum_stack_size());
}
} catch(boost::system::system_error& e) {
cerr<<e.code()<<" "<<e.what()<<endl;
}
}
在orchid中,协程的main函数必须满足函数签名void(orchid::coroutine_handle),如handle_accept所示,其中参数co是协程句柄,代表了当前函数所位于的协程。
在上面的代码中,我们创建了一个acceptor,并让它监听5678端口,然后在"阻塞"等待连接到来,当连接事件到来时,创建一个新的协程来服务新的socket。处理套接字IO的协程如下:
//处理SOCKET IO事件的协程
void handle_io(orchid::coroutine_handle co,socket_ptr sock) {
orchid::tcp_ostream out(*sock,co);
orchid::tcp_istream in(*sock,co);
for(std::string str;std::getline(in, str) && out;) {
out<<str<<endl;
}
}
IO处理协程首先在传入的套接字上创建了一个输入流和一个输出流,分别代表了TCP的输入和输出。然后不断地从输入流中读取一行,并输出到输出流当中。当socket上的TCP连接断开时,输入流和输出流的eof标志为会被置位,因此循环结束,协程退出。
orchid可以使用户以流的形式来操作套接字。输入流和输出流分别提供了std::istream和std::ostream的接口;输入流和输出流是带缓冲的,如果用户需要无缓冲的读写socket或者自建缓冲,可以直接调用orchid::socket的read和write函数。但是需要注意这两个函数会抛出boost::system_error异常来表示错误。
细心的读者可能已经发现,handle_io的函数签名并不满足void(orchid::coroutine_handle),回到handle_accept中,可以发现,实际上我们使用了boost.bind对handle _ io函数进行了适配,使之符合函数签名的要求。
最后是main函数:
int main() {
orchid::scheduler sche;
sche.spawn(handle_accept,orchid::coroutine::minimum_stack_size());//创建协程
sche.run();
}
在上面这个echo server的例子中,我们采用了一种 coroutine per connection 的编程模型,与传统的 thread per connection 模型一样的简洁清晰,但是整个程序实际上运行在同一线程当中。
由于协程的切换开销远远小于线程,因此我们可以轻易的同时启动上千协程来同时服务上千连接,这是 thread per connection的模型很难做到的;在性能方面,整个底层的IO系统实际上是使用boost.asio这种高性能的异步io库实现的。而且与IO所费的时间相比,协程切换的开销基本可以忽略。
因此通过协程,我们可以在保持同步IO模型简洁性的同时,获得近似于异步IO模型的高性能。
posted @
2013-01-01 13:14 江浸月 阅读(6033) |
评论 (5) |
编辑 收藏
2013年1月1日
协程
协程,即协作式程序,其思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态。协程可以在运行期间的某个点上暂停执行,并在恢复运行时从暂停的点上继续执行。 协程已经被证明是一种非常有用的程序组件,不仅被python、lua、ruby等脚本语言广泛采用,而且被新一代面向多核的编程语言如golang rust-lang等采用作为并发的基本单位。 协程可以被认为是一种用户空间线程,与传统的线程相比,有2个主要的优点:
- 与线程不同,协程是自己主动让出CPU,并交付他期望的下一个协程运行,而不是在任何时候都有可能被系统调度打断。因此协程的使用更加清晰易懂,并且多数情况下不需要锁机制。
- 与线程相比,协程的切换由程序控制,发生在用户空间而非内核空间,因此切换的代价非常小。
网络编程模型
首先来简单回顾一下一些常用的网络编程模型。网络编程模型可以大体的分为同步模型和异步模型两类。
同步模型使用阻塞IO模式,在阻塞IO模式下调用read等IO函数时会阻塞线程直到IO完成或失败。
同步模型的典型代表是thread per connection模型,每当阻塞在主线程上的accept调用返回时则创建一个新的线程去服务于新的socket的读/写。这种模型的优点是程序简洁,编写简单;缺点是可伸缩性收到线程数的限制,当连接越来越多时,线程也越来越多,频繁的线程切换会严重拖累性能。
异步模型一般使用非阻塞IO模式,并配合epoll/select/poll等多路复用机制。在非阻塞模式下调用read,如果没有数据可读则立即返回并通知用户没有可读(EAGAIN/EWOULDBLOCK),而非阻塞当前线程。异步模型可以使一个线程同时服务于多个IO对象。
异步模型的典型代表是reactor模型。在reactor模型中,我们将所有要处理的IO事件注册到一个中心的IO多路复用器中(一般为epoll/select/poll),同时主线程阻塞在多路复用器上。一旦有IO事件到来或者就绪,多路复用器返回并将对应的IO事件分发到对应的处理器(即回调函数)中,最后处理器调用read/write函数来进行IO操作。
异步模型的特点是性能和可伸缩性比同步模型要好很多,但是其结构复杂,不易于编写和维护。在异步模型中,IO之前的代码(IO任务的提交者)和IO之后的处理代码(回调函数)是割裂开来的。
协程与网络编程
协程为克服同步模型和异步模型的缺点,并结合他们的优点提供了可能: 现在假设我们有3个协程A,B,C分别要进行数次IO操作。这3个协程运行在同一个调度器或者说线程的上下文中,并依次使用CPU。调度器在其内部维护了一个多路复用器(epoll/select/poll)。
协程A首先运行,当它执行到一个IO操作,但该IO操作并没有立即就绪时,A将该IO事件注册到调度器中,并主动放弃CPU。这时调度器将B切换到CPU上开始执行,同样,当它碰到一个IO操作的时候将IO事件注册到调度器中,并主动放弃CPU。调度器将C切换到cpu上开始执行。当所有协程都被“阻塞”后,调度器检查注册的IO事件是否发生或就绪。假设此时协程B注册的IO时间已经就绪,调度器将恢复B的执行,B将从上次放弃CPU的地方接着向下运行。A和C同理。
这样,对于每一个协程来说,是同步的模型;但是对于整个应用程序来说,却是异步的模型。
好了,原理说完了,我们来看一个实际的例子,echo server。
echo server
在这个例子中,我们将使用orchid库来编写一个echo server。orchid库是一个构建于boost基础上的 协程/网络IO 库。
echo server首先必须要处理连接事件,我们创建一个协程来专门处理连接事件:
typedef boost::shared_ptr<orchid::socket> socket_ptr;
//处理ACCEPT事件的协程
void handle_accept(orchid::coroutine_handle co) {
try {
orchid::acceptor acceptor(co -> get_scheduler().get_io_service());//构建一个acceptor
acceptor.bind_and_listen("5678",true);
for(;;) {
socket_ptr sock(new orchid::socket(co -> get_scheduler().get_io_service()));
acceptor.accept(*sock,co);
//在调度器上创建一个协程来服务新的socket。第一个参数是要创建的协程的main函数,第二个参数是要创建的协程的栈的大小。
co -> get_scheduler().spawn(boost::bind(handle_io,_1,sock),orchid::minimum_stack_size());
}
} catch(boost::system::system_error& e) {
cerr<<e.code()<<" "<<e.what()<<endl;
}
}
在orchid中,协程的main函数必须满足函数签名void(orchid::coroutine_handle),如handle_accept所示,其中参数co是协程句柄,代表了当前函数所位于的协程。
在上面的代码中,我们创建了一个acceptor,并让它监听5678端口,然后在"阻塞"等待连接到来,当连接事件到来时,创建一个新的协程来服务新的socket。处理套接字IO的协程如下:
//处理SOCKET IO事件的协程
void handle_io(orchid::coroutine_handle co,socket_ptr sock) {
orchid::tcp_ostream out(*sock,co);
orchid::tcp_istream in(*sock,co);
for(std::string str;std::getline(in, str) && out;) {
out<<str<<endl;
}
}
IO处理协程首先在传入的套接字上创建了一个输入流和一个输出流,分别代表了TCP的输入和输出。然后不断地从输入流中读取一行,并输出到输出流当中。当socket上的TCP连接断开时,输入流和输出流的eof标志为会被置位,因此循环结束,协程退出。
orchid可以使用户以流的形式来操作套接字。输入流和输出流分别提供了std::istream和std::ostream的接口;输入流和输出流是带缓冲的,如果用户需要无缓冲的读写socket或者自建缓冲,可以直接调用orchid::socket的read和write函数。但是需要注意这两个函数会抛出boost::system_error异常来表示错误。
细心的读者可能已经发现,handle_io的函数签名并不满足void(orchid::coroutine_handle),回到handle_accept中,可以发现,实际上我们使用了boost.bind对handle _ io函数进行了适配,使之符合函数签名的要求。
最后是main函数:
int main() {
orchid::scheduler sche;
sche.spawn(handle_accept,orchid::coroutine::minimum_stack_size());//创建协程
sche.run();
}
在上面这个echo server的例子中,我们采用了一种 coroutine per connection 的编程模型,与传统的 thread per connection 模型一样的简洁清晰,但是整个程序实际上运行在同一线程当中。
由于协程的切换开销远远小于线程,因此我们可以轻易的同时启动上千协程来同时服务上千连接,这是 thread per connection的模型很难做到的;在性能方面,整个底层的IO系统实际上是使用boost.asio这种高性能的异步io库实现的。而且与IO所费的时间相比,协程切换的开销基本可以忽略。
因此通过协程,我们可以在保持同步IO模型简洁性的同时,获得近似于异步IO模型的高性能。
posted @
2013-01-01 13:14 江浸月 阅读(6033) |
评论 (5) |
编辑 收藏
2011年11月28日
转载请注明出处。谢谢
C++11中有很多激动人心的特性,但是相应的使得C++更加复杂。。。
新标准还修改了原有标准库,并增加了很多内容。
在学习新标准的过程中动手写了个 为std::tuple增加格式化/序列化能力的一小段代码
#define DECLARE_TUPLE_SERIALIZATION_FUNCTION(FUNC_NAME,BEG,SEP,END) \
namespace sjdfsjfyttsaihfah6755jsdf554433356sdf{ \
template <typename Tuple,std::size_t N> \
struct tuple_printer \
{ \
static void print(std::ostream& os,const Tuple& t) \
{ \
os<<std::get<std::tuple_size<Tuple>::value - N >(t)<<SEP; \
tuple_printer<Tuple,N-1>::print(os,t); \
} \
}; \
\
template <typename Tuple> \
struct tuple_printer<Tuple,1> \
{ \
static void print(std::ostream& os,const Tuple& t) \
{ \
os<<std::get<std::tuple_size<Tuple>::value-1>(t); \
} \
}; \
} \
template <typename Tuple> \
void FUNC_NAME(std::ostream& os,const Tuple& t) \
{ \
os<<BEG; \
sjdfsjfyttsaihfah6755jsdf554433356sdf::tuple_printer<Tuple,std::tuple_size<Tuple>::value>::print(os,t); \
os<<END; \
}
实现成宏是为了使用起来更方便,可以随意指定 函数名 前缀 分隔符 和 后缀。
使用方法如下:
DECLARE_TUPLE_SERIALIZATION_FUNCTION(serialize_tuple,"<"," , ",">")
int main()
{
int i=10;
auto a = std::make_tuple(3,"lala",i,'c');
serialize_tuple(std::cout,a);
}
输出为:
<3 , "lala" , 10 , c>
测试环境为GCC 4.5,注意编译时候请打开C++0X支持。
posted @
2011-11-28 05:17 江浸月 阅读(1998) |
评论 (0) |
编辑 收藏
2011年8月13日
题目二:
题目我做了下改变,使用了上篇文章中提到的那个类X,代码如下:
1 class X
2 {
3 public:
4 X(){cout<<"default construct"<<endl;}
5 X(int a):i(a){ cout<<"construct "<<i<<endl;}
6 ~X(){ cout<<"desconstruct "<<i<<endl;}
7 X(const X& x):i(x.i)
8 {
9 cout<<"copy construct "<<i<<endl;
10 }
11 X& operator++()
12 {
13 cout<<"operator ++(pre) "<<i<<endl;
14 ++i;
15 return *this;
16 }
17 const X operator++(int)
18 {
19 cout<<"operator ++(post) "<<i<<endl;
20 X x(*this);
21 ++i;
22 return x;
23 }
24 X& operator=(int m)
25 {
26 cout<<"operator =(int)"<<endl;
27 i = m;
28 return *this;
29 }
30 X& operator=(const X& x)
31 {
32 cout<<"operator =(X)"<<endl;
33 i=x.i;
34 return *this;
35 }
36 /////////////////////////
37 friend ostream& operator<<(ostream& os,const X& x)
38 {
39 os<<x.i;
40 return os;
41 }
42 friend X operator+(const X& a,const X& b)
43 {
44 cout<<"operator +"<<endl;
45 return X(a.i+b.i);
46 }
47 //////////////////////////
48 public:
49 int i;
50 };
请问以下代码的输出是什么?
1 X a(10),b(20);
2 X c=a+b;
我们来看一下使用GCC4.5(默认编译选项)以及MSVC9.0(BOTH DEBUG AND RELEASE)编译后的实际运行结果:
construct 10
construct 20
operator +
construct 30
desconstruct 30
desconstruct 20
desconstruct 10
简单分析下这个输出:
construct 10
construct 20 //对应 X a(10),b(20);
operator + //调用“+”操作符
construct 30 //调用X(int){...},44行处
desconstruct 30 //变量c 的析构
desconstruct 20 //变量b 的析构
desconstruct 10 //变量a 的析构
从结果可以看出,整个执行过程中没有输出“operator=”,说明压根没有调用“=”操作符,而且整个过程比我想象的要简洁高效,没有临时对象,没有拷贝构造。
结果为什么会是这样呢?这主要归功于编译器的返回值优化的能力。
有关返回值优化的知识,限于篇幅我就不仔细介绍了,但是需要特别指出的是MSVC9.0只在RELEASE模式下默认开启NRVO,即对具名对象的返回值优化,以及返回值优化里面的一个重要的细节,体现在本例里就是:为什么中整个输出中没有出现"opeartor=",即为什么没调用"="操作符。
现在我们将代码稍微改变一下,改成下面的样子:
X a(10),b(20),c;
c=a+b; //这里我们将c的构造和赋值分开了
执行的结果如下:
construct 10 //构造a
construct 20 //构造b
default construct //构造 c
operator + //调用“+”操作符
construct 30 //调用X(int){...},44行处
operator =(X) //调用“=”操作符
desconstruct 30 //代码45行所建立的临时对象的析构
desconstruct 30 //变量c的析构
desconstruct 20 //变量b的析构
desconstruct 10 //变量c的析构
对比前后的输出结果,可以发现多出以下三行
operator =(X)
desconstruct 30
出现这种差异的原因在于:
定义c的时候会调用默认的构造函数进行初始化,因此第一条语句执行完之后,c已经是一个存在的对象,所以第二条语句并没有权利去直接修改c的内容,必须要通过调用赋值操作符”=“,因此必须要产生一个临时对象。而在第一个例子中,因为执行到第二条语句之前c并没有被创建,所以编译器可以将 表达式a+b的返回值直接构建在c的内存中,从而优化掉临时对象和对“=”的调用。
posted @
2011-08-13 21:38 江浸月 阅读(2087) |
评论 (7) |
编辑 收藏
今年要开始找工作了,本着积累经验的目的,跑去做了下MTK的笔试题,笔试的内容主要是C++。
因为开发中一直使用C++,而且对C++里的高级特性:面向对象,模板等都比较熟悉,还没事喜欢研究下STL,BOOST,所以对自己的C++水平比较自信,因此事先也没做任何准备,就直接去笔试了。本来笔试完了后觉得题目蛮简单的,但是本着认真学习的态度回来后把题目都上机试验了下,结果一下就悲剧了,错的体无完服啊。。。
总结了一下:
1。认真对待,不要小看了笔试题目:做题的时候心想这些笔试题目都很简单啊,很多题目都是扫了一眼就立即写出了答案,结果回来后才发现这些题目都设置了陷阱,让你掉进去就出不来了。
2。C++基础不够扎实。枉我还一天到晚的研究C++的高级特性,结果很多基础的知识却都是一知半解。
特将此次笔试的一些心得和体会记录于此,好提醒自己。下面主要分析几个我做错的题目。题目并非与原题完全一致。
题目一:
int a=10,b=6;
cout<<a+b<<" "<<a++<<" "<<b++;
请说出上述语句的执行结果。
很多人看过这段代码后估计都会直接就写上了 16 10 6 这样的结果吧,但上机实验的输出结果是: 18 10 6
为什么会出现这样的结果,下面是我的分析过程,如果有不对的地方请大家指正。
为了跟踪代码的执行步骤,我设计了一个类X,这个类是对int的模拟,行为方面与int基本一致,除了会打印出一些帮助我们理解的信息,代码如下:
class X
{
public:
X(){cout<<"default construct"<<endl;}
X(int a):i(a){ cout<<"construct "<<i<<endl;}
~X(){ cout<<"desconstruct "<<i<<endl;}
X(const X& x):i(x.i)
{
cout<<"copy construct "<<i<<endl;
}
X& operator++()
{
cout<<"operator ++(pre) "<<i<<endl;
++i;
return *this;
}
const X operator++(int)
{
cout<<"operator ++(post) "<<i<<endl;
X x(*this);
++i;
return x;
}
X& operator=(int m)
{
cout<<"operator =(int)"<<endl;
i = m;
return *this;
}
X& operator=(const X& x)
{
cout<<"operator =(X)"<<endl;
i=x.i;
return *this;
}
/////////////////////////
friend ostream& operator<<(ostream& os,const X& x)
{
os<<x.i;
return os;
}
friend X operator+(const X& a,const X& b)
{
cout<<"operator +"<<endl;
return X(a.i+b.i);
}
//////////////////////////
public:
int i;
};
然后执行以下代码:
X a(10),b(6);
cout<<"sum:" <<a+b<<" a:"<<a++<<" b:"<<b++<<endl;
使用GCC4。5编译后,代码的执行结果如下:
construct 10
construct 6
operator ++(post) 6
copy construct 6
operator ++(post) 10
copy construct 10
operator +
construct 18
sum:18 a:10 b:6
desconstruct 18
desconstruct 10
desconstruct 6
desconstruct 7
desconstruct 11
我们来简单分析下这个执行过程:
construct 10
construct 6 //这两行输出对应于 X a(10),b(6);
operator ++(post) 6
copy construct 6 //表明首先执行了 cout<<"sum:" <<a+b<<" a:"<<a++<<" b:"<<b++<<endl;这句中的 b++这个表达式,
b++这个表达式返回了一个值为6的临时对象,而b本身则变成了7。
operator ++(post) 10
copy construct 10 //这句的分析同上
operator +
construct 18 //对应于表达式 a+b ,可以看到,此时的a和b已经变成了11和7。表达式返回了一个值为18的临时对象。
sum:18 a:10 b:6 //输出的结果,从结果可以看出,实际上打印出的值分别为 a+b,a++和b++三个表达式所返回的临时变量。
desconstruct 18 //a+b 表达式返回的临时变量的析构
desconstruct 10 //a++ 表达式返回的临时变量的析构
desconstruct 6 //b++表达式返回的临时变量的析构
desconstruct 7 //变量a 的析构
desconstruct 11 //变量b的析构
真相大白了。为什么编译器会这样来编译这个表达式呢?
下面2楼的夜风同学给出了正确答案。。为了不误导后面的同学,特此编辑掉。。
上述实验的环境均为GCC4。5 据同学说VS2010执行的结果在DEBUG下和RELEASE下居然分别为:16 10 6 和18 10 6,不过我没有去验证过,有兴趣的同学可以去验证并分析一下。
做这样一道题还是让我收获很多,巩固了C++的基础。
今天就写道这里,后面有时间会陆续放出对其他“陷阱”题目的分析。
(未完待续)
posted @
2011-08-13 17:30 江浸月 阅读(3215) |
评论 (19) |
编辑 收藏
2011年5月26日
首先,BOOST中有4种有关互斥量得概念。
1.LOCKABLE :仅支持排它型所有权
2.TIMEDLOCKABLE:支持带超时的排它型所有权
3.SHAREDLOCKABLE: 支持带超时的排他型所有权和共享型所有权(读写锁)
4.UPGRADELOCKABLE:
支持带超时的排他型所有权和共享型所有权,以及共享型所有权升级为排他型所有权(升级过程阻塞)(也支持降级)
可以看到2强化自1,3强化自2.4强化自3,支持某一概念则一定支持其强化自的概念。
boost::mutex 实现了LOCKABLE概念 (boost::recursive_mutex 是其递归锁的版本)
boost::timed_mutex 实现了TIMEDLOCKABLE概念
(boost::recursive_timed_mutex 是其递归锁的版本)
boost::shared_mutex实现了SHAREDLOCKABLE概念
boost::shared_mutex同样实现了UPGRADELOCKABLE概念
出于提供RAII操作风格和安全等其他一些原因BOOST不希望用户直接调用各种MUTEX类型中的相关接口,而是通过它提供的一些LOCK_TYPE来帮助我们调用。
主要的LOCK_TYPE包括:
boost::unique_lock<LOCKABLE> 针对支持LOCKABLE概念的类型(上述4中MUTEX类型都支持LOCKABLE概念)。以RAII的方式调用该类的lock()
(调用成功后排它的独占该互斥量)和 unlock() 方法。
boost::shared_lock<SHAREDLOCKABLE>针对支持SHAREDLOCKABLE概念的类型,boost::shared_mutex实现了该概念,注意,支持SHAREDLOCKABLE概念的类既支持排他的独占(写锁,通过调用lock unlock系列函数),也支持共享的方式占用(读锁,通过调用lock_shared系列),
shared_lock默认调用
lock_shared系列。
最主要最常用的就是上面这两个LOCK类型,分别代表独占方式和共享方式,其他的就不一一分析了。
下面是个从
http://hi.baidu.com/jrckkyy/blog/item/d7ccb508dfba2e3ce8248817.html此处找到的例子
typedef boost::shared_mutex rwmutex;
typedef boost::shared_lock<rwmutex> readLock;
typedef boost::uniq_lock<rwmutex> writeLock;
rwmutex _rwmutex;
void readOnly()
{
...
{ // 临界区
readLock rdlock(_rwmutex);
...
do something
...
}
...
}
void writeOnly()
{
...
{ // 临界区
writeLock wlock(_rwmutex);
...
do something
...
}
...
}
posted @
2011-05-26 01:10 江浸月 阅读(3917) |
评论 (0) |
编辑 收藏