+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
线程状态
在一个线程的生存期内,可以在多种状态之间转换。不同操作系统可以实现不同的线程模型,定义许多不同的线程状态,每个状
态还可以包含多个子状态。但大体说来,如下几种状态是通用的:
就绪:参与调度,等待被执行。一旦被调度选中,立即开始执行。
运行:占用CPU,正在运行中。
休眠:暂不参与调度,等待特定事件发生。
中止:已经运行完毕,等待回收线程资源(要注意,这个很容易误解,后面解释)。
线程环境
线程存在于进程之中。进程内所有全局资源对于内部每个线程均是可见的。
进程内典型全局资源有如下几种:
代码区。这意味着当前进程空间内所有可见的函数代码,对于每个线程来说也是可见的。
静态存储区。全局变量。静态变量。
动态存储区。也就是堆空间。
线程内典型的局部资源有:
本地栈空间。存放本线程的函数调用栈,函数内部的局部变量等。
部分寄存器变量。例如本线程下一步要执行代码的指针偏移量。
一个进程发起之后,会首先生成一个缺省的线程,通常称这个线程为主线程。C/C++程序中主线程就是通过main函数进入的线程
。由主线程衍生的线程称为从线程,从线程也可以有自己的入口函数,作用相当于主线程的main函数。
这个函数由用户指定。Pthread和winapi中都是通过传入函数指针实现。在指定线程入口函数时,也可以指定入口函数的参数。
就像main函数有固定的格式要求一样,线程的入口函数一般也有固定的格式要求,参数通常都是void *类型,返回类型在
pthread中是void *, winapi中是unsigned int,而且都需要是全局函数。
最常见的线程模型中,除主线程较为特殊之外,其他线程一旦被创建,相互之间就是对等关系 (peer to peer), 不存在隐含的
层次关系。每个进程可以创建的最大线程数由具体实现决定。
为了更好的理解上述概念,下面通过具体代码来详细说明。
线程类接口定义
一个线程类无论具体执行什么任务,其基本的共性无非就是
创建并启动线程
停止线程
另外还有就是能睡,能等,能分离执行(有点拗口,后面再解释)。
还有其他的可以继续加…
将线程的概念加以抽象,可以为其定义如下的类:
文件 thread.h
#ifndef __THREAD__H_
#define __THREAD__H_
class Thread
{
public:
Thread();
virtual ~Thread();
int start (void * = NULL);
void stop();
void sleep (int);
void detach();
void * wait();
protected:
virtual void * run(void *) = 0;
private:
//这部分win和unix略有不同,先不定义,后面再分别实现。
//顺便提一下,我很不习惯写中文注释,这里为了更明白一
//点还是选用中文。
…
};
#endif
Thread::start()函数是线程启动函数,其输入参数是无类型指针。
Thread::stop()函数中止当前线程。
Thread::sleep()函数让当前线程休眠给定时间,单位为秒。
Thread::run()函数是用于实现线程类的线程函数调用。
Thread::detach()和thread::wait()函数涉及的概念略复杂一些。在稍后再做解释。
Thread类是一个虚基类,派生类可以重载自己的线程函数。下面是一个例子。
示例程序
代码写的都不够精致,暴力类型转换比较多,欢迎有闲阶级美化,谢过了先。
文件create.h
#ifndef __CREATOR__H_
#define __CREATOR__H_
#include
#include "thread.h"
class Create: public Thread
{
protected:
void * run(void * param)
{
char * msg = (char*) param;
printf ("%s\n", msg);
//sleep(100); 可以试着取消这行注释,看看结果有什么不同。
printf("One day past.\n");
return NULL;
}
};
#endif
然后,实现一个main函数,来看看具体效果:
文件Genesis.cpp
#include
#include "create.h"
int main(int argc, char** argv)
{
Create monday;
Create tuesday;
printf("At the first God made the heaven and the earth.\n");
monday.start("Naming the light, Day, and the dark, Night, the first day.");
tuesday.start("Gave the arch the name of Heaven, the second day.");
printf("These are the generations of the heaven and the earth.\n");
return 0;
}
编译运行,程序输出如下:
At the first God made the heaven and the earth.
These are the generations of the heaven and the earth.
令人惊奇的是,由周一和周二对象创建的子线程似乎并没有执行!这是为什么呢?别急,在最后的printf语句之前加上如下语句
:
monday.wait();
tuesday.wait();
重新编译运行,新的输出如下:
At the first God made the heaven and the earth.
Naming the light, Day, and the dark, Night, the first day.
One day past.
Gave the arch the name of Heaven, the second day.
One day past.
These are the generations of the heaven and the earth.
为了说明这个问题,需要了解前面没有解释的Thread::detach()和Thread::wait()两个函数的含义。
无论在windows中,还是Posix中,主线程和子线程的默认关系是:
无论子线程执行完毕与否,一旦主线程执行完毕退出,所有子线程执行都会终止。这时整个进程结束或僵死(部分线程保持一种
终止执行但还未销毁的状态,而进程必须在其所有线程销毁后销毁,这时进程处于僵死状态),在第一个例子的输出中,可以看
到子线程还来不及执行完毕,主线程的main()函数就已经执行完毕,从而所有子线程终止。
需要强调的是,线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态(请回顾上面说的线程状态),但千万要记住
的是,进入终止态后,为线程分配的系统资源并不一定已经释放,而且可能在系统重启之前,一直都不能释放。终止态的线程,
仍旧作为一个线程实体存在与操作系统中。(这点在win和unix中是一致的。)而什么时候销毁线程,取决于线程属性。
通常,这种终止方式并非我们所期望的结果,而且一个潜在的问题是未执行完就终止的子线程,除了作为线程实体占用系统资源
之外,其线程函数所拥有的资源(申请的动态内存,打开的文件,打开的网络端口等)也不一定能释放。所以,针对这个问题,
主线程和子线程之间通常定义两种关系:
可会合(joinable)。这种关系下,主线程需要明确执行等待操作。在子线程结束后,主线程的等待操作执行完毕,子线程
和主线程会合。这时主线程继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程,Thread类中,这个操作通过
在主线程的线程函数内部调用子线程对象的wait()函数实现。这也就是上面加上三个wait()调用后显示正确的原因。必须强调的
是,即使子线程能够在主线程之前执行完毕,进入终止态,也必需显示执行会合操作,否则,系统永远不会主动销毁线程,分配
给该线程的系统资源(线程id或句柄,线程管理相关的系统资源)也永远不会释放。
相分离(detached)。顾名思义,这表示子线程无需和主线程会合,也就是相分离的。这种情况下,子线程一旦进入终止态
,系统立即销毁线程,回收资源。无需在主线程内调用wait()实现会合。Thread类中,调用detach()使线程进入detached状态。
这种方式常用在线程数较多的情况,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是很困
难或者不可能的。所以在并发子线程较多的情况下,这种方式也会经常使用。
缺省情况下,创建的线程都是可会合的。可会合的线程可以通过调用detach()方法变成相分离的线程。但反向则不行。
UNIX实现
文件 thread.h
#ifndef __THREAD__H_
#define __THREAD__H_
class Thread
{
public:
Thread();
virtual ~Thread();
int start (void * = NULL);
void stop();
void sleep (int);
void detach();
void * wait();
protected:
virtual void * run(void *) = 0;
private:
pthread_t handle;
bool started;
bool detached;
void * threadFuncParam;
friend void * threadFunc(void *);
};
//pthread中线程函数必须是一个全局函数,为了解决这个问题
//将其声明为静态,以防止此文件之外的代码直接调用这个函数。
//此处实现采用了称为Virtual friend function idiom 的方法。
Static void * threadFunc(void *);
#endif
文件thread.cpp
#include
#include
#include “thread.h”
static void * threadFunc (void * threadObject)
{
Thread * thread = (Thread *) threadObject;
return thread->run(thread->threadFuncParam);
}
Thread::Thread()
{
started = detached = false;
}
Thread::~Thread()
{
stop();
}
bool Thread::start(void * param)
{
pthread_attr_t attributes;
pthread_attr_init(&attributes);
if (detached)
{
pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
}
threadFuncParam = param;
if (pthread_create(&handle, &attributes, threadFunc, this) == 0)
{
started = true;
}
pthread_attr_destroy(&attribute);
}
void Thread::detach()
{
if (started && !detached)
{
pthread_detach(handle);
}
detached = true;
}
void * Thread::wait()
{
void * status = NULL;
if (started && !detached)
{
pthread_join(handle, &status);
}
return status;
}
void Thread::stop()
{
if (started && !detached)
{
pthread_cancel(handle);
pthread_detach(handle);
detached = true;
}
}
void Thread::sleep(unsigned int milliSeconds)
{
timeval timeout = { milliSeconds/1000, millisecond%1000};
select(0, NULL, NULL, NULL, &timeout);
}
Windows实现
文件thread.h
#ifndef _THREAD_SPECIFICAL_H__
#define _THREAD_SPECIFICAL_H__
#include
static unsigned int __stdcall threadFunction(void *);
class Thread {
friend unsigned int __stdcall threadFunction(void *);
public:
Thread();
virtual ~Thread();
int start(void * = NULL);
void * wait();
void stop();
void detach();
static void sleep(unsigned int);
protected:
virtual void * run(void *) = 0;
private:
HANDLE threadHandle;
bool started;
bool detached;
void * param;
unsigned int threadID;
};
#endif
文件thread.cpp
#include "stdafx.h"
#include
#include "thread.h"
unsigned int __stdcall threadFunction(void * object)
{
Thread * thread = (Thread *) object;
return (unsigned int ) thread->run(thread->param);
}
Thread::Thread()
{
started = false;
detached = false;
}
Thread::~Thread()
{
stop();
}
int Thread::start(void* pra)
{
if (!started)
{
param = pra;
if (threadHandle = (HANDLE)_beginthreadex(NULL, 0, threadFunction, this, 0, &threadID))
{
if (detached)
{
CloseHandle(threadHandle);
}
started = true;
}
}
return started;
}
//wait for current thread to end.
void * Thread::wait()
{
DWORD status = (DWORD) NULL;
if (started && !detached)
{
WaitForSingleObject(threadHandle, INFINITE);
GetExitCodeThread(threadHandle, &status);
CloseHandle(threadHandle);
detached = true;
}
return (void *)status;
}
void Thread::detach()
{
if (started && !detached)
{
CloseHandle(threadHandle);
}
detached = true;
}
void Thread::stop()
{
if (started && !detached)
{
TerminateThread(threadHandle, 0);
//Closing a thread handle does not terminate
//the associated thread.
//To remove a thread object, you must terminate the thread,
//then close all handles to the thread.
//The thread object remains in the system until
//the thread has terminated and all handles to it have been
//closed through a call to CloseHandle
CloseHandle(threadHandle);
detached = true;
}
}
void Thread::sleep(unsigned int delay)
{
::Sleep(delay);
}
小结
本节的主要目的是帮助入门者建立基本的线程概念,以此为基础,抽象出一个最小接口的通用线程类。在示例程序部分,初学者
可以体会到并行和串行程序执行的差异。有兴趣的话,大家可以在现有线程类的基础上,做进一步的扩展和尝试。如果觉得对线
程的概念需要进一步细化,大家可以进一步扩展和完善现有Thread类。
想更进一步了解的话,一个建议是,可以去看看其他语言,其他平台的线程库中,线程类抽象了哪些概念。比如Java, perl等跨
平台语言中是如何定义的,微软从winapi到dotnet中是如何支持多线程的,其线程类是如何定义的。这样有助于更好的理解线程
的模型和基础概念。
另外,也鼓励大家多动手写写代码,在此基础上尝试写一些代码,也会有助于更好的理解多线程程序的特点。比如,先开始的线
程不一定先结束。线程的执行可能会交替进行。把printf替换为cout可能会有新的发现,等等。
每个子线程一旦被创建,就被赋予了自己的生命。管理不好的话,一只特例独行的猪是非常让人头痛的。
对于初学者而言,编写多线程程序可能会遇到很多令人手足无措的bug。往往还没到考虑效率,避免死锁等阶段就问题百出,而
且很难理解和调试。这是非常正常的,请不要气馁,后续文章会尽量解释各种常见问题的原因,引导大家避免常见错误。目前能
想到入门阶段常遇到的问题是:
内存泄漏,系统资源泄漏。
程序执行结果混乱,但是在某些点插入sleep语句后结果又正确了。
程序crash, 但移除或添加部分无关语句后,整个程序正常运行(假相)。
多线程程序执行结果完全不合逻辑,出于预期。
本文至此,如果自己动手改改,试一些例子,对多线程程序应该多少有一些感性认识了。刚开始只要把基本概念弄懂了,后面可
以一步一步搭建出很复杂的类。不过刚开始不要贪多,否则会欲速则不达,越弄越糊涂。
join() :一直阻塞等待。
timed_join() 阻塞等待线程结束。或者阻塞一定的时间段,然后不管线程是否结束都返回。
detach() 线程执行体分离,但是线程会继续执行。
yield() 指示当前线程放弃时间片,允许其他的线程执行。
bind:可以把库函数所需的参数绑定到一个函数对象中,而function则可以存储bind表达式的结果,
interrupt():中断正在被执行的线程。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
thread的成员函数interrupt()允许正在执行的线程被中断,被中断的线程会抛出一个thread_interrupted异常,它是一个空类,
不是std::exception或boost::exception的子类。thread_interrupted异常应该在线程执行函数里捕捉和处理,如果线程不处理这个异常,那么默认会中止线程的执行。
hello1
hello2
thread interrupted!
由运行结果可知,线程在执行了两次循环之后中断执行。
上面程序中使用了boost::this_thread::sleep()函数,如果换成windows API函数Sleep(1000),重新运行,则发现线程并没有终止。读者可自行试验。
这就说明线程并不是在任何时候都可以中断的。
线程中断点:
线程并非在任何时候都可以中断的,thread库定义了若干个中断点,只有当线程执行到中断点的时候才可以被中断,一个线程可以有若干个线程中断点。
thread库定义了9个中断点,它们都是函数,如下:
thread::join();
thread::timed_join();this_thread::sleep();
this_thread::interruption_point();
这些中断点的前8个都是某种形式的等待函数,表明线程在阻塞的时候可以被中断。而最后一个this_thread::interruption_point();则是一个特殊的中断点函数,它并不等待,只是起到一个标签的作用,表示线程执行到这个地方可以被中断。
注:在xp环境下使用this_thread::sleep的时候会报错,
ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
scopted_ptr: 一旦获取用户的管理权,就无法取回.
template
class scopted_ptr{
private:
T * px;
scopted_ptr (scopted_ptr const &);
scopted_ptr & operator = (scopted_ptr const& ); //禁止复制,保证了scopted_ptr 的所有权.
public:
explicit scopted_ptr(T* p =0);//p必须是new的对象.
~scopted_ptr();
void reset(T*p =0);
T& operator*() const;
T * operator->()const;
T * get() const;//小心使用,这将scopted_ptr脱离,不能对这个指针做deleted操作,否则scopted_ptr析构时会再次删除,程序会崩溃.
operator unspecified-bool-type() const;
void swap(scopted_ptr & b);
};
auto_ptr : 可以转让,同时也失去了管理权。
scopted_array:
template
public :
explicit scoped_array(T* p = 0);
~scoped_array();
void reset(T* p =0);
T& operator[] (std::ptrdiff_t i) const;
T* get() const;
operator unspecified-bool-type() const;
void swap(scoped_array& b);
};
shared_ptr;
template
public:
typedef T element_type;
shared_ptr();
shared_ptr(Y * p);//获得指向类型T的指针P的管理权,同时引用计数器置为1.这个要求Y必须能够转换为T类型。
template
template
~shared_ptr();
shared_ptr(shared_ptr const& r);//从另外一个shared_ptr 获取指针管理权,同时引用计数加1,结果是两个shared_ptr共享一个指针管理权。
template
shared_ptr & operator = (shared_ptr const & r);
template
template
void reset();//引用计数减少1,停止对指针的共享。
template
template
T& operator*() const;
T* operator->() const;
T* get() const;
bool unique() const; //
long use_count() const; //
operator unspecified-bool-type() const;
void swap(shared_ptr& b);
};
//////////////////
shared_ptr
assert(spi); //bool 语境中隐式转换为bool值。
shared_ptr
shared_ptr不能使用诸如static_cast
他们与标准转型类似,不过返回的是shared_ptr。
//工厂函数
make_shared
template
shared_ptr
#include
int main(){
shared_ptr
shared_ptr
}
桥接模式:就是利用纯虚函数在子类中实现。
里式替换原则:任何基类出现的地方,子类一定可以出现。
虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象。
shared_ptr 删除器:
fg: socket_t *s = open_socket();
shared_ptr
也可以在函数前面加上地址操作符& , shared_ptr
shared_ptr
删除器的高级用法:
由于空指针可以是任何指针类型,因此share_ptr
void any_function(void *p ){
cout<<“some operation”<
int main(){
shared_ptr
}
weak_ptr: shared_ptr的助手. 获得资源的观测权,但是没有共享资源,它的构造不会引起引用计数的增加
template
{
plublic :
weak_ptr();
template
weak_ptr(weak_ptr const& r);
~weak_ptr();
weak_ptr & operator=(weak_ptr const& r);
long use_count() const;
bool expired() const; //等价于 use_count == 0;
shared_ptr
void reset();
void swap(weak_ptr
};
fg:
shared_ptr
assert(sp.use_count()==1);
weak_ptr
assert(sp.use_count()==1);
if(!wp.expired()){
shared_ptr
*sp2 = 100;
assert(wp.use_count() ==2);
}
assert(wp.use_count() == 1);
sp.reset();
assert(wp.expired());
assert(!wp.lock());
获取this的shared_ptr ;
#include
#include
class self_shared: public enable_shared_from_this
public:
self_shared(int n):x(n){}
int x;
void print(){cout<<"self_shared"<< x <
int main(){
shared_ptr
sp->print();
p->x =1000;
}
//intrusive_ptr 侵入式引用计数指针。
//101
pool : 只分配普通数据类型,不能用于类和对象。
template
class pool {
public:
explicit pool(size_type requested_size);
~pool();
size_type get_requested_size() const;
void *malloc();
void *ordered_malloc();
void *ordered_malloc(size_type n);
bool is_from(void * chunk) const;//测试是否是从这个 内存池分出去的。
void free(void *chunk);
void ordered_free(void* chunk);
void ordered_free(void * chunks,size_type n);
bool release_memory();//让内存池释放已经未分配的内存,但已经分配的内存不受影响。
bool purge_memory();//强制释放所有分配的内存。pool的析构就是调用这个,一般不要手动调用。
}
fg:
#include
using namespace std;
int main(){
pool<> p1(sizeof(int));
int *p = (int *)p1.malloc();
assert(p1.is_from(p));
p1.free(p);
for(int i=0;i<100;++i){
p1.ordered_malloc(10);
}
}
栈:函数内部局部变量
堆:new 分配的
自由储存区:malloc分配的
全局静态存储区:全局变量和静态变量。
常态存储区:常量,不允许修改。
object_pool :用于类实例(对象)的内存池
#include
using namespace boost;
template
class object_pool: protected pool{
public:
object_pool();
~object_pool();
//malloc 和 free 被调用时不调用构造函数和析构函数,也就是说只操作一块原始的内存块。尽量少用。
element_type * malloc();
void free(element_type* p);
bool is_from(element_type *p) const;
element_type * construct(...); //直接在内存池中创建对象。
void destory(element_type* p);
}
#include
using namespace std;
struct demo_class{
public :
int a,b,c;
demo_class(int x =1, int y =2, int z=3):a(x),b(y),c(z){}
};
int main(){
object_pool
demo_class *p = p1.malloc();
assert(p1.is_from(p));
assert(p->a!=1 || p->b!=2 || p->c != 3); //p都没有初始化
p = p1.construct(7,8,9);
assert(p->a ==7);
object_pool
for(int i=0;i<10;++i){
string *ps = pls.construct("hello object_pool");
cout <<*ps<
}
singleton_pool : 可以分配简单数据类型(POD)的内存指针,但是它是一个单件,并提供线程安全。不会自动释放所占空间。除非手动调用release_memory()或purge_memory();
#include
using namespace boost;
template
class singleton_pool{
public: static bool is_from(void * ptr);
static void * malloc();
static void * ordered_malloc();
static void * ordered_malloc(size_type n);
static void free(void * ptr);
static void ordered_free(void * ptr);
static void free(void* ptr);
static void ordered_free(void *ptr,size_type n);
static bool release_memory();
static bool purge_memory();
}
单件模式:单件创建分为两种,一:将一个公共构造函数改为私有的,二是:保留类的公共构造函数,通过一个静态成员来决定是否返回一个实例。
BOOST_AUTO 的功能与auto 相似。
为无效值提供了一个更好的处理方式:
optional: 包装可能产生无效值的对象,实现未初始化的概念。
#include
using namespace boost;
template
class optional {
public:
optional();
optional(T const& v);
optional(bool condition , T v);
optional & operator = (T const& rhs);
T* operator->();
T& operator*();
T& get();
T* get_ptr();
T const& get_value_or(T const& default) const;
bool operator!() const;
}
/////////////////////////////////
#include
using namespace boost;
optional
return optional
}
optional
return optional
}
int main(){
optional
if(d){
cout<<*d<
if(!d){
cout<<"no result"<
}
////////////////////////////////
工厂函数:make_pair(),make_shared(),make_optional():可以根据参数类型自动推导optional的类型,用来辅助创造optional对象。
optional
optional
make_optional()无法推导出T引用类型的optional对象,因此需要一个optional
#include
using namespace std;
int main(){
BOOST_AUTO(x,make_optional(5)));
assert(*x == 5);
BOOST_AUTO(y,make_optional
assert(!y);
}
optional
in_place_factory.
#include
#include
using namespace boost;
int main(){
// 就地创建string对象,不要临时对象string.
optional
cout<<*ops<
boost 赋值: assign;
int main(){
using namespace boost::assign;//启用assign库的功能。
vector
v += 1,2,3,4,5,6*6;
set
s += "cpp","java","c#","python";
map
m+= make_pair(1,"one"),make_pair(2,"two");
}
operator += 仅限于应用STL中定义的标准容器。
operator() 提供了更加通用的解决方案。不能直接使用operate(),应当使用assign库提供的三个辅助函数insert(),push_front(),push_back().返回一个代理
对象list_inserter.
#include
int main(){
using namespace boost::assign;
vertor
push_back(v)(1)(2)(3);
list
push_front(l)("cpp")("java")("c#");
set
insert(s)(3.14)(0.618);
map
insert(m)(1,"one")(2,"two");
}
对于拥有push_back()或push_front()成员函数的容器(vertor,list),可以调用assign::push_back()或者assign::push_front,
而对于set和map,则只能使用assign::insert().
初始化容器元素:
list_of() 函数的用法与之前的insert(),push_back()等函数,也重载了括号,逗号操作符。很智能。
减少重复输入:
template
generic_list& repeat(std::size_t sz, U u);
template
generic_list& repeat_fun(std::size_t sz,Nullary_function fun);
template
generic_list& range(SinglePassIterator first, SinglePassIterator last);
template
generic_list& range(const SinglePassRange& r);
单件模式:
boost.pool的单件实现。
singleton_default.
template
struct singleton_default {
public :
typedef T object_type;
static object_type & instance();
}
fg:
#include
using boost::details::pool::singleton_default;
class point{
public:
point(int a=0,int b=0,int c=0):x(a),y(b),z(c){
cout<<"point ctor"<
~point(){
cout<<"point dtor"<
};
int main(){
cout<<"main start"<
origin::instance.print();
}
boost.serialzation的单件实现
在序列库serialization中另一个单件实现类:singleton.位于 boost::serialization.
singleton:
template
class singleton: public boost::noncopyable{
public:
static const T& get_const_instance();
static T& get_mutable_instance();
}
两种用法:
一:
#include
using boost::serialization::singleton;
class point{...}
int main(){
cout<<"main start"<
origin::get_const_instance().print();
origin::get_mutable_instance().print();
cout<<"main() finished"<
二:通过继承的方式。
#include
using boost::serialization::singleton;
class point : public singleton
int main(){
cout<<"main start"<
origin::get_const_instance().print();
origin::get_mutable_instance().print();
cout<<"main() finished"<
tribool:三态的布尔逻辑。
字符串与文本处理:
lexical_cast
并发编程:
mutex: 独占式互斥量
try_mutex
timed_mutex : 也是独占试互斥量,但提供超时锁定功能。
recursive_mutex 递归式互斥量,可以多次锁定。相应的也要多次解锁。
recursive_try_mutex
recursive_timed_mutex 递归是互斥量
shared_mutex: multiple-reader/single-writer型共享互斥量。
loack_guard 他们在构造时锁定互斥量,在析构时自动解锁。
basic_atom:
template
class basic_atom: noncopyable{
private:
T n;
typedef mutex mutex_t;
mutex_t mu;
public :
T operator ++ (){
mutex_t::scoped_lock lock(mu);
return ++n;
}
operator T(){
return n;
}
}
typedef basic_atom
atom_int x;
cout<<++x;
join() :一直阻塞等待。
timed_join() 阻塞等待线程结束。或者阻塞一定的时间段,然后不管线程是否结束都返回。
detach() 线程执行体分离,但是线程会继续执行。
yield() 指示当前线程放弃时间片,允许其他的线程执行。
bind:可以把库函数所需的参数绑定到一个函数对象中,而function则可以存储bind表达式的结果,
interrupt():中断正在被执行的线程。
bind:
bind(f,1,2) => f(1,2)
bind(f,_1,5)(x) => f(x,5);
如果想传入变量的引用。
int i=5;
bind(f,ref(i),_1);
bind(f,cref(i),1);
bind
boost::bind(boost::type
通过pointers to members使用bind
bind将传入的成员(数据成员和成员函数)指针作为第一个参数,其行为如同使用boost::mem_fn将成员指针转换为一个函数对象,即:
bind(&X::f, args); 等价于bind
bind中的第一个参数不参与计算过程,假设如下程序想要实现对于向量v中的每个函数指针,传入一个参数 5:
typedef void (*pf)(int);
std::vector
std::for_each(v.begin(), v.end(), bind(_1, 5));
上述程序并没有实现我们想要的结果:可以通过使用一个帮助函数对象apply,该对象可以将bind的第一个参数作为一个函数对象,如下:
typedef void (*pf)(int);
std::vector
std::for_each(v.begin(), v.end(), bind(apply
thread 库在字命名空间this_thread提供了一组函数和类共同完成线程的中断启用和禁用。
线程组:
thread 提供thread_group用于管理一组线程。就像一个线程池。
条件变量:
thread 库提供的另一种等待的同步机制,可以实现线程的通信,它必须与互斥量配合使用,等待另外一个线程中某个事件的发生,然后线程才能继续执行。
thread: condition_variable 和 condition_variable_any,一般情况下使用condition_varible_any。
缓冲区buffer 使用了两个变量cond_put 和 cond_get() 的处理流程与cond_put类似。
#include
class buffer{
private:
mutex mu;
condition_variable_any cond_put;
condition_variable_any cond_get;
stack
int un_read,capacity;
bool is_full(){
return un_read == capacity;
}
bool is_empty(){
return un_read == 0;
}
public:
buffer(size_t n):un_read(0),capacity(n){}
void put(int x){
{
mutex::scoped_lock lock(mu);
while(is_full){
{
mutex_t::scoped_lock lock(io_mu);
cout<<"full waiting..."<
cond_put.wait(mu);
}
stk.push(x);
++un_read;
}
cond_get.notify_one();
}
void get(int *x){
{
mutex::scoped_lock lock(mu);
while(is_empty()){
{
mutex::scoped_lock(io_mu);
cout<<"empty waiting"<
cond_get.wait(mu);
}
--un_read;
*x = stk.top();
stk.pop();
}
cond_put.notify_one();
}
};
共享互斥量:
shared_mutex 不同于mutex和recursive_mutex,它允许线程获取多个共享所有权和一个专享所有权,实现读写机制,即多个线程读一个线程写。
读线程时:shared_lock
future; 在很多情况下线程不仅仅要工作还要执行工作,他还可能返回一些计算结果。
funture 使用packaged_task 和 promise 两个模版类来包装异步调用。unique_future 和 shared_future来获取异步调用结果。
future 还提供wait_for_any() 和 wait_for_all()
仅初始化一次:
使多个线程在操作初始化函数时只能有一个线程成功执行。
once_flag of = BOOST_INIT;
void call_func(){
call_once(of,init_count);
}
barrier: 是thread基于条件变量的另一种同步机制,可以用于多个线程同步。当线程执行到barrier时必须要等待,直到所有的线程都到达这个点时才能继续执行。
智能所:boost::mutex::scoped_lock.
线程本地存储:
有时候函数使用了局部变量或者全局变量,因此不能用于多线程,因为无法保证静态变量在线程环境下重入时是否正确。
thread库使用了thread_specific_ptr实现了可移植的线程本地存储机制。
thread_specific_ptr 智能指针。
定时器:
deadline_timer有两种形式的构造函数,都要求有一个io_service 对象,用于提交IO请求,第二个是定时器的终止时间,可以是posix_time。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
scopted_ptr
scoped_ptr是一个行为很类似标准库中的auto_ptr智能指针,它包装了new操作符在椎上分配的动态对象,能够保证动态创建的对象在任何时候都能够被正确的删除。
但是scoped_ptr的所有权更加严格,不允许转让,对其赋值和拷贝都是不合法行为,因而显得更轻巧和快捷。
scoped_ptr重载了operator*()和operator->()解引用操作符*和箭头操作符->,因此可以把scoped_ptr对象如同指针一样使用。如果scoped_ptr保存的空指针,那么这两个操作的行为未定义。
scoped_ptr不能在两个scoped_ptr之间、scoped_ptr与原始指针之间或空指针之间进行了相等或不相等测试。operator ==和operator !=都被声明为私有。
下面是scoped_ptr类摘要
scoped_ptr有两个好处:一是使代码更加清晰简单,而简单意味着更少的错误。二是它并没有增加多余的操作,安全的同时保证效率,可以获得与原始指针同样的速度。
使用范例:
运行结果:
Hello,ajioy!
12
100
scoped_ptr == null
Open file:/tmp/a.txt
close file
与auto_ptr的区别
两者的根本性区别在于指针的所有权。auto_ptr特意被设计成可以移交所有权的,而scoped_ptr刚好相反。
scoped_ptr明确地表明了代码原始者的意图:只能在定义的作用域内使用,不可转让。
scoped_ptr在大多情况下可以与auto_ptr互换,且可以从一个auto_ptr获得指针的所有权(同时auto_ptr失去管理权)。
两者均有缺陷,都不能作为容器的元素,不过原因不同:auto_ptr是因为它的转移语义,而scoped_ptr是因为不支持拷贝和赋值,不符合容器对元素类型的要求。
使用示例:
运行结果:
20,10
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
首先看看boost::thread的构造函数吧,boost::thread有两个构造函数:
(1)thread():构造一个表示当前执行线程的线程对象;
(2)explicit thread(const boost::function0& threadfunc):
boost::function0可以简单看为:一个无返回(返回void),无参数的函数。这里的函数也可以是类重载operator()构成的函数;该构造函数传入的是函数对象而并非是函数指针,这样一个具有一般函数特性的类也能作为参数传入,在下面有例子。
第一种方式:最简单方法
void hello() { std::cout << "Hello world, I''m a thread!" << std::endl; } int main(int argc, char* argv[]) { boost::thread thrd(&hello); thrd.join(); return 0; }
第二种方式:复杂类型对象作为参数来创建线程:
boost::mutex io_mutex; struct count { count(int id) : id(id) { } void operator()() { for (int i = 0; i < 10; ++i) { boost::mutex::scoped_lock lock(io_mutex); std::cout << id << ": " << i << std::endl; } } int id; }; int main(int argc, char* argv[]) { boost::thread thrd1(count(1)); boost::thread thrd2(count(2)); thrd1.join(); thrd2.join(); return 0; }
第三种方式:在类内部创建线程;
class HelloWorld { public: static void hello() { std::cout << "Hello world, I''m a thread!" << std::endl; } static void start() { boost::thread thrd( hello ); thrd.join(); } }; int main(int argc, char* argv[]) { HelloWorld::start(); return 0; } 在这里start()和hello()方法都必须是static方法。
(2)如果要求start()和hello()方法不能是静态方法则采用下面的方法创建线程:
class HelloWorld { public: void hello() { std::cout << "Hello world, I''m a thread!" << std::endl; } void start() { boost::function0< void> f = boost::bind(&HelloWorld::hello,this); boost::thread thrd( f ); thrd.join(); } }; int main(int argc, char* argv[]) { HelloWorld hello; hello.start(); return 0; }
第四种方法:用类内部函数在类外部创建线程;
class HelloWorld { public: void hello(const std::string& str) { std::cout < } }; int main(int argc, char* argv[]) { HelloWorld obj; boost::thread thrd( boost::bind(&HelloWorld::hello,&obj,"Hello world, I''m a thread!" ) ) ; thrd.join(); return 0; }
如果线程需要绑定的函数有参数则需要使用boost::bind。比如想使用 boost::thread创建一个线程来执行函数:void f(int i),如果这样写:boost::thread thrd(f)是不对的,因为thread构造函数声明接受的是一个没有参数且返回类型为void的型别,而且不提供参数i的值f也无法运行,这时就可以写:boost::thread thrd(boost::bind(f,1))。涉及到有参函数的绑定问题基本上都是boost::thread、boost::function、boost::bind结合起来使用。
任何写过多线程程序的人都知道避免不同线程同时访问共享区域的重要性。如果一个线程要改变共享区域中某个数据,而与此同时另一线程正在读这个数据,那么结果将是未定义的。为了避免这种情况的发生就要使用一些特殊的原始类型和操作。其中最基本的就是互斥体(mutex,mutual exclusion的缩写)。一个互斥体一次只允许一个线程访问共享区。当一个线程想要访问共享区时,首先要做的就是锁住(lock)互斥体。如果其他的线程已经锁住了互斥体,那么就必须先等那个线程将互斥体解锁,这样就保证了同一时刻只有一个线程能访问共享区域。
互斥体的概念有不少变种。Boost线程库支持两大类互斥体,包括简单互斥体(simple mutex)和递归互斥体(recursive mutex)。如果同一个线程对互斥体上了两次锁,就会发生死锁(deadlock),也就是说所有的等待解锁的线程将一直等下去。有了递归互斥体,单个线程就可以对互斥体多次上锁,当然也必须解锁同样次数来保证其他线程可以对这个互斥体上锁。
在这两大类互斥体中,对于线程如何上锁还有多个变种。一个线程可以有三种方法来对一个互斥体加锁:
似乎最佳的互斥体类型是递归互斥体,它可以使用所有三种上锁形式。然而每一个变种都是有代价的。所以Boost线程库允许你根据不同的需要使用最有效率的互斥体类型。Boost线程库提供了6中互斥体类型,下面是按照效率进行排序:
boost::mutex,
boost::try_mutex,
boost::timed_mutex,
boost::recursive_mutex,
boost::recursive_try_mutex,
boost::recursive_timed_mutex
如果互斥体上锁之后没有解锁就会发生死锁。这是一个很普遍的错误,Boost线程库就是要将其变成不可能(至少时很困难)。直接对互斥体上锁和解锁对于Boost线程库的用户来说是不可能的。mutex类通过teypdef定义在RAII中实现的类型来实现互斥体的上锁和解锁。这也就是大家知道的Scope Lock模式。为了构造这些类型,要传入一个互斥体的引用。构造函数对互斥体加锁,析构函数对互斥体解锁。C++保证了析构函数一定会被调用,所以即使是有异常抛出,互斥体也总是会被正确的解锁。
有的时候仅仅依靠锁住共享资源来使用它是不够的。有时候共享资源只有某些状态的时候才能够使用。比方说,某个线程如果要从堆栈中读取数据,那么如果栈中没有数据就必须等待数据被压栈。这种情况下的同步使用互斥体是不够的。另一种同步的方式--条件变量,就可以使用在这种情况下。
条件变量的使用总是和互斥体及共享资源联系在一起的。线程首先锁住互斥体,然后检验共享资源的状态是否处于可使用的状态。如果不是,那么线程就要等待条件变量。要指向这样的操作就必须在等待的时候将互斥体解锁,以便其他线程可以访问共享资源并改变其状态。它还得保证从等到得线程返回时互斥体是被上锁得。当另一个线程改变了共享资源的状态时,它就要通知正在等待条件变量得线程,并将之返回等待的线程。
List4是一个使用了boost::condition的简单例子。有一个实现了有界缓存区的类和一个固定大小的先进先出的容器。由于使用了互斥体boost::mutex,这个缓存区是线程安全的。put和get使用条件变量来保证线程等待完成操作所必须的状态。有两个线程被创建,一个在buffer中放入100个整数,另一个将它们从buffer中取出。这个有界的缓存一次只能存放10个整数,所以这两个线程必须周期性的等待另一个线程。为了验证这一点,put和get在std::cout中输出诊断语句。最后,当两个线程结束后,main函数也就执行完毕了。
大多数函数都不是可重入的。这也就是说在某一个线程已经调用了一个函数时,如果你再调用同一个函数,那么这样是不安全的。一个不可重入的函数通过连续的调用来保存静态变量或者是返回一个指向静态数据的指针。 举例来说,std::strtok就是不可重入的,因为它使用静态变量来保存要被分割成符号的字符串。
有两种方法可以让不可重用的函数变成可重用的函数。第一种方法就是改变接口,用指针或引用代替原先使用静态数据的地方。比方说,POSIX定义了strok_r,std::strtok中的一个可重入的变量,它用一个额外的char**参数来代替静态数据。这种方法很简单,而且提供了可能的最佳效果。但是这样必须改变公共接口,也就意味着必须改代码。另一种方法不用改变公有接口,而是用本地存储线程(thread local storage)来代替静态数据(有时也被成为特殊线程存储,thread-specific storage)。
Boost线程库提供了智能指针boost::thread_specific_ptr来访问本地存储线程。每一个线程第一次使用这个智能指针的实例时,它的初值是NULL,所以必须要先检查这个它的只是否为空,并且为它赋值。Boost线程库保证本地存储线程中保存的数据会在线程结束后被清除。
List5是一个使用boost::thread_specific_ptr的简单例子。其中创建了两个线程来初始化本地存储线程,并有10次循环,每一次都会增加智能指针指向的值,并将其输出到std::cout上(由于std::cout是一个共享资源,所以通过互斥体进行同步)。main线程等待这两个线程结束后就退出。从这个例子输出可以明白的看出每个线程都处理属于自己的数据实例,尽管它们都是使用同一个boost::thread_specific_ptr。
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT