join ------------------ detach

join detach swap

指向容器的迭代器、引用和指针

thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

thread::join()是个简单暴力的方法,主线程等待子进程期间什么都不能做,一般情形是主线程创建thread object后做自己的工作而不是简单停留在join上。thread::join()还会清理子线程相关的内存空间,此后thread object将不再和这个子线程相关了,即thread object不再joinable了,所以join对于一个子线程来说只可以被调用一次,为了实现更精细的线程等待机制,可以使用条件变量等机制。

      异常环境下join,假设主线程在一个函数f()里面创建thread object,接着f()又调用其它函数g(),那么确保在g()以任何方式下退出主线程都能join子线程。如:若g()通过异常退出,那么f()需要捕捉异常后join

[cpp] view plain copy

    #include<iostream>  
    #include<boost/thread.hpp>  
    void do_something(int& i){  
        i++;  
    }  
    class func{  
        public:  
            func(int& i):i_(i){}  
            void operator() (){  
                for(int j=0;j<100;j++)  
                    do_something(i_);  
            }  
        public:  
            int& i_;  
    };  
    void do_something_in_current_thread(){}  
    void f(){  
        int local=0;  
        func my_func(local);  
        boost::thread t(my_func);  
        try{  
            do_something_in_current_thread();  
        }  
        catch(...){  
            t.join();//确保在异常条件下join子线程  
            throw;  
        }  
        t.join();  
    }  
    int main(){  
        f();  
        return 0;  
    }  



      上面的方法看起来笨重,有个解决办法是采用RAII(资源获取即初始化),将一个thread object通过栈对象A管理,在栈对象A析构时调用thread::join.按照局部对象析构是构造的逆序,栈对象A析构完成后再析构thread object。如下:

[cpp] view plain copy

    #include<iostream>  
    #include<boost/noncopyable.hpp>  
    #include<boost/thread.hpp>  
    using namespace std;  
    class thread_guard:boost::noncopyable{  
        public:  
            explicit thread_guard(boost::thread& t):t_(t){}  
            ~thread_guard(){  
                if(t_.joinable()){//检测是很有必要的,因为thread::join只能调用一次,要防止其它地方意外join了  
                   t_.join();  
                }  
            }  
            //thread_guard(const thread_guard&)=delete;//c++11中这样声明表示禁用copy constructor需要-std=c++0x支持,这里采用boost::noncopyable已经禁止了拷贝和复制  
            //thread_guard& operator=(const thread_guard&)=delete;  
        private:  
            boost::thread& t_;  
    };  
    void do_something(int& i){  
        i++;  
    }  
    class func{  
        public:  
            func(int& i):i_(i){}  
            void operator()(){  
                for(int j=0;j<100;j++)  
                    do_something(i_);  
            }  
        public:  
            int& i_;  
    };  
    void do_something_in_current_thread(){}  
    void fun(){  
        int local=0;  
        func my_func(local);  
        boost::thread t(my_func);  
        thread_guard g(t);  
        do_something_in_current_thread();  
    }  
    int main(){  
        fun();  
        return 0;  
    }  


    说明:禁止拷贝和复制的原因是防止栈对象thread_guard在超出了thread object对象生命期的地方使用。如果detach一个线程则没有上述这么麻烦,必经detach后就不管子线程了。


     thread::detach()后:没有直接方法与线程通信,不可能wait了,不可能有任何thread object指向这个线程。线程变成了后台进程(孤儿进程),在Linux将由init接管,在c++中由库接管。若不确定一个线程是否有thread object指向它,那么请先用thread::joinable()检测后再thread::detach()就想前面代码中的joinable检测一样。

      考虑一个情形:通常我们在word中编辑文件A时,点击"新建"按钮后会出现新的窗口继续编辑文件B。这里A和B是相对独立的,A"新建"相当于开启一个线程去供B使用,其后A马上detach这个线程。代码如下:

[cpp] view plain copy

    #include<iostream>  
    #include<boost/thread.hpp>  
    void open_document_and_display_gui(const std::string& filename){}//“新建”这一操作  
    bool done_eaditing(){//是否完成编辑  
        return true;  
    }  
    enum command_type{open_new_document};//用户命令,这里只有一个就是“新建”  
    class user_command{//用户命令封装  
        public:  
            user_command():type(open_new_document){}  
        public:  
            command_type type;  
    };  
    user_command get_user_input(){//获取用户命令,这里就是获取"新建“  
        return user_command();  
    }  
    std::string get_filename_from_user(){  
        return "foo.doc";  
    }  
    void process_user_input(const user_command& cmd){}//处理用户的普通输入,即文档编写  
      
    void edit_document(const std::string& filename){//假设初始时文档A执行edit_document,发现用户有”新建“命令到达,则开启一个线程去供文档B使用,且立即detach这个线程  
        open_document_and_display_gui(filename);  
        while(!done_eaditing()){  
            user_command cmd=get_user_input();  
            if(cmd.type==open_new_document){  
                const std::string new_name=get_filename_from_user();  
                boost::thread t(edit_document,new_name);//文档B也是采用同样的线程函数,这里可以看出thread可以接收参数,还有一个方法参考pthread_create将参数封装在一个函数对象中,然后传给thread  
                t.detach();//立即执行detach,这里可以肯定thread t和线程相关,故不需要检测joinable  
            }  
            else  
                process_user_input(cmd);//普通文档编辑  
        }  
    }  
    int main(){  
        edit_document("bar.doc");  
        return 0;  
    }

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



线程状态
在一个线程的生存期内,可以在多种状态之间转换。不同操作系统可以实现不同的线程模型,定义许多不同的线程状态,每个状

态还可以包含多个子状态。但大体说来,如下几种状态是通用的:
       就绪:参与调度,等待被执行。一旦被调度选中,立即开始执行。
       运行:占用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 <stdio.h>
#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 <stdio.h>
#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 <pthread.h>
#include <sys/time.h>
#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 <windows.h>

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 <process.h>
#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():中断正在被执行的线程。


+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


1. 概述

线程就是,在同一程序同一时间内允许执行不同函数的离散处理队列。 这使得一个长时间去进行某种特殊运算的函数在执行时不阻碍其他的函数变得十分重要。 线程实际上允许同时执行两种函数,而这两个函数不必相互等待。

一旦一个应用程序启动,它仅包含一个默认线程。 此线程执行main() 函数。 在main()中被调用的函数则按这个线程的上下文顺序地执行。 这样的程序称为单线程程序。

反之,那些创建新的线程的程序就是多线程程序。 他们不仅可以在同一时间执行多个函数,而且这在如今多核盛行的时代显得尤为重要。 既然多核允许同时执行多个函数,这就使得对开发人员相应地使用这种处理能力提出了要求。 然而线程一直被用来当并发地执行多个函数,开发人员现在不得不仔细地构建应用来支持这种并发。 多线程编程知识也因此在多核系统时代变得越来越重要。

2. 线程管理

2.1 情景1(test_thread_wait1)

在这个库最重要的一个类就是boost::thread,它是在boost/thread.hpp里定义的,用来创建一个新线程。下面的示例来说明如何运用它。

新建线程里执行的那个函数的名称被传递到boost::thread的构造函数。 一旦上述示例中的变量t 被创建,该 thread() 函数就在其所在线程中被立即执行。 同时在test_thread_wait1()里也并发地执行该 threadfun1() 。

为了防止程序终止,就需要对新建线程调用join() 方法。join() 方法是一个阻塞调用:它可以暂停当前线程,直到调用 join() 的线程运行结束。这就使得test_thread_wait1()函数一直会等待到 threadfun1()运行结束。

正如在上面的例子中看到,一个特定的线程可以通过诸如t的变量访问,通过这个变量等待着它的使用 join() 方法终止。 但是,即使 t 越界或者析构了,该线程也将继续执行。 一个线程总是在一开始就绑定到一个类型为 boost::thread 的变量,但是一旦创建,就不在取决于它。 甚至还存在着一个叫detach()的方法,允许类型为boost::thread 的变量从它对应的线程里分离。 当然了,像join()的方法之后也就不能被调用,因为这个变量不再是一个有效的线程。

任何一个函数内可以做的事情也可以在一个线程内完成。 归根结底,一个线程只不过是一个函数,除了它是同时执行的。 在上述例子中,使用一个循环把5个数字写入标准输出流。 为了减缓输出,每一个循环中调用wait() 函数让执行延迟了一秒。 wait() 可以调用一个名为sleep() 的函数,这个函数也来自于 Boost.Thread,位于 boost::this_thread 名空间内。

sleep() 要么在预计的一段时间或一个特定的时间点后时才让线程继续执行。 通过传递一个类型为boost::posix_time::seconds 的对象,在这个例子里我们指定了一段时间。 boost::posix_time::seconds 来自于Boost.DateTime库,它被 Boost.Thread 用来管理和处理时间的数据。

虽然前面的例子说明了如何等待一个不同的线程,但下面的例子演示了如何通过所谓的中断点让一个线程中断。

2.2 情景2(test_thread_wait2())

在一个线程对象上调用 interrupt() 会中断相应的线程。在这方面,中断意味着一个类型为boost::thread_interrupted的异常,它会在这个线程中抛出。然后这只有在线程达到中断点时才会发生。

如果给定的线程不包含任何中断点,简单调用interrupt() 就不会起作用。每当一个线程中断点,它就会检查interrupt() 是否被调用过。只有被调用过了, boost::thread_interrupted 异常才会相应地抛出。

Boost.Thread定义了一系列的中断点,例如sleep()函数。由于sleep()在这个例子里被调用了五次,该线程就检查了五次它是否应该被中断。然而sleep()之间的调用,却不能使线程中断。

一旦该程序被执行,它只会打印三个数字到标准输出流。这是由于在test_thread_wait2()里3秒后调用 interrupt()方法。因此,相应的线程被中断,并抛出一个boost::thread_interrupted 异常。 这个异常在线程内也被正确地捕获,catch处理虽然是空的。由于thread() 函数在处理程序后返回,线程也被终止。这反过来也将终止整个程序,因为test_thread_wait2()等待该线程使用join()终止该线程。

Boost.Thread定义包括上述 sleep()函数十个中断。有了这些中断点,线程可以很容易及时中断。然而,他们并不总是最佳的选择,因为中断点必须事前读入以检查boost::thread_interrupted异常。

3.示例

  1. void wait(int seconds)  
  2. {  
  3.     boost::this_thread::sleep(boost::posix_time::seconds(seconds));  
  4. }  
  5.   
  6. void threadfun1()  
  7. {  
  8.     for (int i = 0; i < 5; ++i)  
  9.     {  
  10.         wait(1);  
  11.         PRINT_DEBUG(i);  
  12.     }  
  13. }  
  14.   
  15. void threadfun2()   
  16. {  
  17.     try  
  18.     {  
  19.         for (int i = 0; i < 5; ++i)  
  20.         {  
  21.             wait(1);  
  22.             PRINT_DEBUG(i);  
  23.         }  
  24.     }  
  25.     catch (boost::thread_interrupted&)  
  26.     {  
  27.         PRINT_DEBUG("thread_interrupted");  
  28.     }  
  29. }  
  30.   
  31. void test_thread_wait1()  
  32. {  
  33.     boost::thread t(&threadfun1);  
  34.     // join()方法是一个阻塞调用:它可以暂停当前线程,直到调用join()的线程运行结束。  
  35.     t.join();  
  36. }  
  37. 比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。


  38.  

ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss

ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss

thread的成员函数interrupt()允许正在执行的线程被中断,被中断的线程会抛出一个thread_interrupted异常,它是一个空类,

不是std::exception或boost::exception的子类。thread_interrupted异常应该在线程执行函数里捕捉和处理,如果线程不处理这个异常,那么默认会中止线程的执行。

  1. #include "stdafx.h"  
  2. #include <windows.h>  
  3. #include <iostream>  
  4. #include <boost/thread.hpp>  
  5. #include <boost/atomic.hpp>  
  6.   
  7. boost::mutex io_mu;//io流操作锁  
  8.   
  9. void to_interrupt(boost::atomic_int &x,const std::string &str)  
  10. {  
  11.     try  
  12.     {  
  13.         for(int i=0;i<5;++i)  
  14.         {  
  15.             boost::this_thread::sleep(boost::posix_time::seconds(1));//等待1s  
  16.             //Sleep(1000);//等待1s  
  17.             boost::mutex::scoped_lock lock(io_mu);//锁定io流操作  
  18.             std::cout<<str<<++x<<std::endl;  
  19.         }  
  20.     }  
  21.     catch (boost::thread_interrupted& )//捕获线程中断异常  
  22.     {  
  23.         std::cout<<"thread interrupted!"<<std::endl;  
  24.     }  
  25. }  
  26.   
  27. int _tmain(int argc, _TCHAR* argv[])  
  28. {  
  29.     boost::atomic_int x(0);  
  30.     boost::thread t(to_interrupt,ref(x),"hello");  
  31.     boost::this_thread::sleep(boost::posix_time::seconds(2));//休眠2s  
  32.     t.interrupt();//要求线程中断执行  
  33.     t.join();//由于线程已经中断,所以立即返回  
  34.     getchar();  
  35.     return 0;  
  36. }  
程序运行结果如下:

hello1
hello2
thread interrupted!

由运行结果可知,线程在执行了两次循环之后中断执行。

上面程序中使用了boost::this_thread::sleep()函数,如果换成windows API函数Sleep(1000),重新运行,则发现线程并没有终止。读者可自行试验。

这就说明线程并不是在任何时候都可以中断的。

线程中断点:

线程并非在任何时候都可以中断的,thread库定义了若干个中断点,只有当线程执行到中断点的时候才可以被中断,一个线程可以有若干个线程中断点。

thread库定义了9个中断点,它们都是函数,如下:

thread::join();

thread::timed_join();
condition_variable::wait();
condition_variable::timed_wait();
condition_variable_any::wait();
condition_variable_any::timed_wait();
thread::sleep();

this_thread::sleep();

this_thread::interruption_point();

这些中断点的前8个都是某种形式的等待函数,表明线程在阻塞的时候可以被中断。而最后一个this_thread::interruption_point();则是一个特殊的中断点函数,它并不等待,只是起到一个标签的作用,表示线程执行到这个地方可以被中断。

注:在xp环境下使用this_thread::sleep的时候会报错,

      无法定位程序输入点GetTickCount64 在动态链接库kernel32.dll上 错误

解决方法:在stdafx.h中加 #define _WIN32_WINNT 0x0501

ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss

ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss

  1. void test_thread_wait2()  
  2. {  
  3.     boost::thread t(&threadfun2);  
  4.     wait(3);  
  5.     t.interrupt();  
  6.     t.join();  
  7. }  
  8.   
  9. void test_thread_wait3()  
  10. {  
  11.     boost::thread t(&threadfun2);  
  12.     // timed_join()方法同样也是一个阻塞调用:它可以暂停当前线程,  
  13.     // 直到调用join()的线程运行结束或者超时  
  14.     t.timed_join(boost::posix_time::seconds(3));  
  15. }  
  16.   
  17. void test_thread_wait4()  
  18. {  
  19.     boost::thread t(&threadfun2);  
  20.     wait(3);  
  21.     // 当thread 与线程执行体分离时,线程执行体将不受影响地继续执行,  
  22.     // 直到运行结束,或者随主线程一起结束。  
  23.     t.detach();  
  24.     // 此时join无作用  
  25.     t.join();  
  26.     // t不再标识任何线程 {Not-any-thread}  
  27.     assert(t.get_id() == boost::thread::id());  

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


scopted_ptr: 一旦获取用户的管理权,就无法取回.
template<class T>
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<class T> class scopted_array {
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<class T> class shared_ptr{
public:
typedef T element_type;
shared_ptr();
shared_ptr(Y * p);//获得指向类型T的指针P的管理权,同时引用计数器置为1.这个要求Y必须能够转换为T类型。
template<class Y> explicit shared_ptr(Y* p);
template<class Y,class D> shared_ptr(Y*p, D d);//行为类似shared_ptr(Y*p),但是使用参数d指定了析构时的定制删除器。而不是简单的delete.
~shared_ptr();

shared_ptr(shared_ptr const& r);//从另外一个shared_ptr 获取指针管理权,同时引用计数加1,结果是两个shared_ptr共享一个指针管理权。
template<class Y>explicit shared_ptr(std::auto_ptr<Y> & r);//从一个auto_ptr获得引用计数的管理权,引用计数置为1,同时auto_ptr自动失去管理权.

shared_ptr & operator = (shared_ptr const & r);
template<class Y>shared_ptr & operator = (shared_ptr<Y> const& r);
template<class Y>shared_ptr & operator = (std::auto_ptr<Y> & r);

void reset();//引用计数减少1,停止对指针的共享。
template<class Y> void reset(Y* p);//带参数的reset,原指针引用计数减少1的同时改为管理另外一个指针。
template<class Y,class D> void reset(Y *p,D d);

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<int> spi(new int);
assert(spi); //bool 语境中隐式转换为bool值。
shared_ptr<string> sps(new string("smart"));
shared_ptr不能使用诸如static_cast<T*>(p.get())的形式这将提供了类似转型函数static_pointer_cast<T>(),const_pointer_cast<T>()和dynamic_pointer_cast<T>(),
他们与标准转型类似,不过返回的是shared_ptr。

//工厂函数
make_shared<T>() 来消除显示的调用new。
template<class T, class... Args>
shared_ptr<T> make_shared(Args && ... args); //最多可以接受10个参数。
#include <boost/make_shared.hpp>
int main(){
shared_ptr<string> sp = make_shared<string>("make_shared");
shared_ptr<vector<int> >spv = make_shared<vector<int> >(10,2);
}

桥接模式:就是利用纯虚函数在子类中实现。
里式替换原则:任何基类出现的地方,子类一定可以出现。

虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象。

shared_ptr 删除器:
fg: socket_t *s = open_socket();
shared_ptr<socket_t>p(s,close_socket);//只要传入删除器即可
也可以在函数前面加上地址操作符& , shared_ptr<socket_t>p(s,&close_socket);

shared_ptr<void> :就像一个泛型指针容器.拥有容纳任意类型的能力。

删除器的高级用法:
由于空指针可以是任何指针类型,因此share_ptr<void>还可以实现退出作用域时调用任意函数。
void any_function(void *p ){
cout<<“some operation”<<endl;
}
int main(){
shared_ptr<void>p((void*)0,any_function);
}

weak_ptr: shared_ptr的助手. 获得资源的观测权,但是没有共享资源,它的构造不会引起引用计数的增加
template<class T> class weak_ptr
{
plublic :
weak_ptr();
template<class Y>weak_ptr(shared_ptr<Y> const& r);
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<T> lock() const; //expired()返回true,可以从被观测的shared_ptr获得一个可用的对象,从而可以操作资源。
void reset();
void swap(weak_ptr<T> & b);
};

fg:
shared_ptr<int> sp(new int(10));
assert(sp.use_count()==1);
weak_ptr<int>wp(sp);
assert(sp.use_count()==1);
if(!wp.expired()){
shared_ptr<int>sp2=wp.lock();
*sp2 = 100;
assert(wp.use_count() ==2);
}
assert(wp.use_count() == 1);
sp.reset();
assert(wp.expired());
assert(!wp.lock());

获取this的shared_ptr ;
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
class self_shared: public enable_shared_from_this<self_shared>{
public:
self_shared(int n):x(n){}
int x;
void print(){cout<<"self_shared"<< x <<endl;}
};
int main(){
shared_ptr<self_shared>sp = make_shared<self_shared>(314);
sp->print();
p->x =1000;
}

//intrusive_ptr 侵入式引用计数指针。

//101
pool : 只分配普通数据类型,不能用于类和对象。
template<typename UserAllocator =...>
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 <boost/pool/pool.h>
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 <boost/poool/object_pool.hpp>
using namespace boost;

template<typename ElementType>
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<boost/pool/object_pool.hpp>
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>p1;
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<string> pls;//连续分配string 对象的内存池。
for(int i=0;i<10;++i){
string *ps = pls.construct("hello object_pool");
cout <<*ps<<endl;
}
}

singleton_pool : 可以分配简单数据类型(POD)的内存指针,但是它是一个单件,并提供线程安全。不会自动释放所占空间。除非手动调用release_memory()或purge_memory();
#include <boost/pool/singleton_pool.hpp>
using namespace boost;

template<typename Tag,usigned RequestedSize> // RequestedSize 指示pool分配的大小。
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: 包装可能产生无效值的对象,实现未初始化的概念。<boost/optional.hpp>
#include <boost/optional.hpp>
using namespace boost;

template<class T>
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 <boost/optional.hpp>
using namespace boost;
optional<double> cacl(int x){
return optional<double>(x!=0,1.0/x);
}
optional<double> sqrt_op(double x){
return optional<double>(x>0,sqrt(x));
}
int main(){
optional<double> d = calc(10);
if(d){
cout<<*d<<endl;
}
if(!d){
cout<<"no result"<<endl;
}
}

工厂函数:make_pair(),make_shared(),make_optional():可以根据参数类型自动推导optional的类型,用来辅助创造optional对象。
optional<T> make_optional(T const& v);
optional<T> make_optional(bool condition, T const& v);

make_optional()无法推导出T引用类型的optional对象,因此需要一个optional<T&>的对象,就不能使用make_optional()函数。
#include<boost/optional.hpp>
using namespace std;
int main(){
BOOST_AUTO(x,make_optional(5)));
assert(*x == 5);
BOOST_AUTO(y,make_optional<double>((*x>10),1.0));
assert(!y);
}

optional<T> 要求类型T 具有拷贝意义,因为内部会保存值的拷贝,但是有时候拷贝的代价会很高。是种浪费,因此有了optional就地创建
in_place_factory.
#include <boost/optional.hpp>
#include <boost/utility/im_place_factory.hpp>
using namespace boost;
int main(){
// 就地创建string对象,不要临时对象string.
optional<string> ops(in_place("test in_place_factory"));
cout<<*ops<<endl;
}

boost 赋值: assign;
int main(){
using namespace boost::assign;//启用assign库的功能。
vector<int> v;
v += 1,2,3,4,5,6*6;

set<string> s;
s += "cpp","java","c#","python";
map<int,string>m;
m+= make_pair(1,"one"),make_pair(2,"two");
}
operator += 仅限于应用STL中定义的标准容器。
operator() 提供了更加通用的解决方案。不能直接使用operate(),应当使用assign库提供的三个辅助函数insert(),push_front(),push_back().返回一个代理
对象list_inserter.
#include <boost/assign.hpp>
int main(){
using namespace boost::assign;
vertor<int> v;
push_back(v)(1)(2)(3);
list<string> l;
push_front(l)("cpp")("java")("c#");
set<double>s;
insert(s)(3.14)(0.618);
map<int,string>m;
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<class U>
generic_list& repeat(std::size_t sz, U u);
template<class Nullary_function>
generic_list& repeat_fun(std::size_t sz,Nullary_function fun);

template<class SinglePassIterator>
generic_list& range(SinglePassIterator first, SinglePassIterator last);

template<class SinglePassRange >
generic_list& range(const SinglePassRange& r);

单件模式:
boost.pool的单件实现。
singleton_default.
template<typename T>
struct singleton_default {
public :
typedef T object_type;
static object_type & instance();
}
fg:
#include <boost/pool/detail/singleton.hpp>
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"<<endl;
}
~point(){
cout<<"point dtor"<<endl;
}
};
int main(){
cout<<"main start"<<endl;
typedef singleton_default<point> origin;
origin::instance.print();
}

boost.serialzation的单件实现
在序列库serialization中另一个单件实现类:singleton.位于 boost::serialization.
singleton:
template<typename T>
class singleton: public boost::noncopyable{
public:
static const T& get_const_instance();
static T& get_mutable_instance();
}

两种用法:
一:
#include<boost/serialization/singleton.hpp>
using boost::serialization::singleton;
class point{...}
int main(){
cout<<"main start"<<endl;
typedef singleton<point> origin;
origin::get_const_instance().print();
origin::get_mutable_instance().print();
cout<<"main() finished"<<endl;
}
二:通过继承的方式。
#include<boost/serialization/singleton.hpp>
using boost::serialization::singleton;
class point : public singleton<point> {...} //注意这里
int main(){
cout<<"main start"<<endl;
typedef singleton<point> origin;
origin::get_const_instance().print();
origin::get_mutable_instance().print();
cout<<"main() finished"<<endl;
}
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 <typename T>
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<int> atom_int;
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<int>(f,_1,_1)(x) 有些编译器不支持这个用法。
boost::bind(boost::type<int>(),f,_1,_1)(x)

通过pointers to members使用bind
bind将传入的成员(数据成员和成员函数)指针作为第一个参数,其行为如同使用boost::mem_fn将成员指针转换为一个函数对象,即:
bind(&X::f, args); 等价于bind<R>(mem_fn(&X::f), args),其中R为X::f的返回类型(成员函数)或类型(数据成员)。

bind中的第一个参数不参与计算过程,假设如下程序想要实现对于向量v中的每个函数指针,传入一个参数 5:
typedef void (*pf)(int);
std::vector<pf> v;
std::for_each(v.begin(), v.end(), bind(_1, 5));
上述程序并没有实现我们想要的结果:可以通过使用一个帮助函数对象apply,该对象可以将bind的第一个参数作为一个函数对象,如下:
typedef void (*pf)(int);
std::vector<pf> v;
std::for_each(v.begin(), v.end(), bind(apply<void>(), _1, 5));

thread 库在字命名空间this_thread提供了一组函数和类共同完成线程的中断启用和禁用。

线程组:
thread 提供thread_group用于管理一组线程。就像一个线程池。

条件变量:
thread 库提供的另一种等待的同步机制,可以实现线程的通信,它必须与互斥量配合使用,等待另外一个线程中某个事件的发生,然后线程才能继续执行。
thread: condition_variable 和 condition_variable_any,一般情况下使用condition_varible_any。

缓冲区buffer 使用了两个变量cond_put 和 cond_get() 的处理流程与cond_put类似。
#include <stack>
class buffer{
private:
mutex mu;
condition_variable_any cond_put;
condition_variable_any cond_get;
stack<int> stk;
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..."<<endl;
}
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"<<endl;
}
cond_get.wait(mu);
}
--un_read;
*x = stk.top();
stk.pop();
}
cond_put.notify_one();
}
};
共享互斥量:
shared_mutex 不同于mutex和recursive_mutex,它允许线程获取多个共享所有权和一个专享所有权,实现读写机制,即多个线程读一个线程写。
读线程时:shared_lock<shared_mutex> 写线程时:unique_lock<shared_mutex>.

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类摘要

  1. template<class T> class scoped_ptr // noncopyable  
  2. {  
  3. private:  
  4.   
  5.     T * px;  
  6.     scoped_ptr(scoped_ptr const &);  
  7.     scoped_ptr & operator=(scoped_ptr const &);  
  8.     typedef scoped_ptr<T> this_type;  
  9.     void operator==( scoped_ptr const& ) const;  
  10.     void operator!=( scoped_ptr const& ) const;  
  11.   
  12. public:  
  13.   
  14.     typedef T element_type;  
  15.     explicit scoped_ptr( T * p = 0 ); // never throws  
  16.     explicit scoped_ptr( std::auto_ptr<T> p ); // never throws  
  17.   
  18.     ~scoped_ptr() // never throws在其作用域结束时自动删除保存的指针对象,从而正确地回收资源  
  19.     void reset(T * p = 0) // never throws删除原来保存的指针,再保存新的指针p,如果p是空指针,那scoped_ptr将不持有任何指针  
  20.     {  
  21.         BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors  
  22.         this_type(p).swap(*this);  
  23.     }  
  24.   
  25.     T & operator*() const // never throws  
  26.     {  
  27.         BOOST_ASSERT( px != 0 );  
  28.         return *px;  
  29.     }  
  30.   
  31.     T * operator->() const // never throws  
  32.     {  
  33.         BOOST_ASSERT( px != 0 );  
  34.         return px;  
  35.     }  
  36.   
  37.     T * get() const // never throws返回scoped_ptr内部保存的原始指针  
  38.     {  
  39.         return px;  
  40.     }  
  41.   
  42.     void swap(scoped_ptr & b);// never throws交换两个scoped_ptr保存的原始指针  
  43. };  

scoped_ptr有两个好处:一是使代码更加清晰简单,而简单意味着更少的错误。二是它并没有增加多余的操作,安全的同时保证效率,可以获得与原始指针同样的速度。

使用范例:

 

  1. #include <iostream>  
  2. #include <string>  
  3. #include <boost/smart_ptr.hpp>  
  4. using namespace boost;  
  5. using namespace std;  
  6. struct posix_file{  
  7.   posix_file(const char* file_name){  
  8.    cout << "Open file:" << file_name << endl;  
  9.    }  
  10.    ~posix_file(){cout << "close file" << endl;}  
  11. };  
  12. int main(){  
  13.     /* 
  14.     //note:Don't try to use 'delete' again, 
  15.     //scoped_ptr help us destory resource automatically 
  16.     //scoped_ptr is a pointer-behavior-like object,but it isn't a pointer 
  17.     //Even if you use 'delete' it wouldn't work 
  18.     //scoped_ptr do not support assignment and copy 
  19.     //it can only use operator * and -> ,sp++ ,sp = sp2.etc. are illegal usage here 
  20.     */  
  21.     /*exmple1*/  
  22.    scoped_ptr<string> sp(new string("Hello,ajioy!"));  
  23.    cout << *sp << endl;  
  24.    cout << sp->size() << endl;  
  25.      
  26.    /*exmaple2*/  
  27.    scoped_ptr<int> p(new int);//equal int *p;  
  28.    if(p){ //test p is valid  
  29.     *p = 100;  
  30.     cout << *p << endl;  
  31.    }  
  32.    p.reset();//make p null  
  33.    assert(p == 0);  
  34.    if(!p)  
  35.    {cout << "scoped_ptr == null" << endl;}  
  36.    scoped_ptr<posix_file> fp(new posix_file("/tmp/a.txt"));  
  37.    //destructor scoped_ptr here  
  38.    //p and fp are deleted automatically  
  39.       
  40. }  


运行结果:

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是因为不支持拷贝和赋值,不符合容器对元素类型的要求。

使用示例:

  1. #include <iostream>  
  2. #include <boost/smart_ptr.hpp>  
  3. using namespace boost;  
  4. using namespace std;  
  5. int main(){  
  6.   auto_ptr<int> ap(new int(10));  
  7.   scoped_ptr<int> sp(ap);//get original pointer from auto_ptr   
  8.   assert(ap.get() == 0);//The original auto_ptr no longer owns the pointer  
  9.     
  10.   ap.reset(new int(20));//auto_ptr holds a new pointer  
  11.   cout << *ap << "," << *sp << endl;  
  12.     
  13.   auto_ptr<int> ap2;  
  14.   ap2 = ap;//ap2 gets the orginal pointer from ap,transfer ownership  
  15.   assert(ap.get() == 0);//ap no longer owns a pointer  
  16.   scoped_ptr<int> sp2;  
  17.  // sp2 = sp; it won't compile  
  18. }  


运行结果:

20,10

EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE

标准C++线程即将到来。CUJ预言它将衍生自Boost线程库,现在就由Bill带领我们探索一下Boost线程库。

就在几年前,用多线程执行程序还是一件非比寻常的事。然而今天互联网应用服务程序普遍使用多线程来提高与多客户链接时的效率;为了达到最大的吞吐量,事务服务器在单独的线程上运行服务程序;GUI应用程序将那些费时,复杂的处理以线程的形式单独运行,以此来保证用户界面能够及时响应用户的操作。这样使用多线程的例子还有很多。

 

但是C++标准并没有涉及到多线程,这让程序员们开始怀疑是否可能写出多线程的C++程序。尽管不可能写出符合标准的多线程程序,但是程序员们还是会使用支持多线程的操作系统提供的多线程库来写出多线程C++程序。但是这样做至少有两个问题:这些库大部分都是用C语言完成的,如果在C++程序中要使用这些库就必须十分小心;还有,每一个操作系统都有自己的一套支持多线程的类库。因此,这样写出来得代码是没有标准可循的,也不是到处都适用的(non-portable)。Boost线程库就是为了解决所有这些问题而设计的。

Boost是由C++标准委员会类库工作组成员发起,致力于为C++开发新的类库的组织。现在它已经有近2000名成员。许多库都可以在Boost源码的发布版本中找到。为了使这些类库是线程安全的(thread-safe),Boost线程库被创建了。

许多C++专家都投身于Boost线程库的开发中。所有接口的设计都是从0开始的,并不是C线程API的简单封装。许多C++特性(比如构造函数和析构函数,函数对象(function object)和模板)都被使用在其中以使接口更加灵活。现在的版本可以在POSIX,Win32和Macintosh Carbon平台下工作。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">1 创建线程
就像std::fstream类就代表一个文件一样,boost::thread类就代表一个可执行的线程。缺省构造函数创建一个代表当前执行线程的实例。一个重载的构造函数以一个不需任何参数的函数对象作为参数,并且没有返回值。这个构造函数创建一个新的可执行线程,它调用了那个函数对象。

 

起先,大家认为传统C创建线程的方法似乎比这样的设计更有用,因为C创建线程的时候会传入一个void*指针,通过这种方法就可以传入数据。然而,由于Boost线程库是使用函数对象来代替函数指针,那么函数对象本身就可以携带线程所需的数据。这种方法更具灵活性,也是类型安全(type-safe)的。当和Boost.Bind这样的功能库一起使用时,这样的方法就可以让你传递任意数量的数据给新建的线程。

 

目前, 由Boost线程库创建的线程对象功能还不是很强大。事实上它只能做两项操作。线程对象可以方便使用==和!=进行比较来确定它们是否是代表同一个线程;你还可以调用boost::thread::join来等待线程执行完毕。其他一些线程库可以让你对线程做一些其他操作(比如设置优先级,甚至是取消线程)。然而,由于要在普遍适用(portable)的接口中加入这些操作不是简单的事,目前仍在讨论如何将这些操组加入到Boost线程库中。

 

Listing1展示了boost::thread类的一个最简单的用法。 新建的线程只是简单的在std::out上打印“hello,world”,main函数在它执行完毕之后结束。


例1:
#include <boost/thread/thread.hpp>
#include <iostream>

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;
}
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">2 互斥体
任何写过多线程程序的人都知道避免不同线程同时访问共享区域的重要性。如果一个线程要改变共享区域中某个数据,而与此同时另一线程正在读这个数据,那么结果将是未定义的。为了避免这种情况的发生就要使用一些特殊的原始类型和操作。其中最基本的就是互斥体(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++保证了析构函数一定会被调用,所以即使是有异常抛出,互斥体也总是会被正确的解锁。

这种方法保证正确的使用互斥体。然而,有一点必须注意:尽管Scope Lock模式可以保证互斥体被解锁,但是它并没有保证在异常抛出之后贡献资源仍是可用的。所以就像执行单线程程序一样,必须保证异常不会导致程序状态异常。另外,这个已经上锁的对象不能传递给另一个线程,因为它们维护的状态并没有禁止这样做。

 

List2给出了一个使用boost::mutex的最简单的例子。例子中共创建了两个新的线程,每个线程都有10次循环,在std::cout上打印出线程id和当前循环的次数,而main函数等待这两个线程执行完才结束。std::cout就是共享资源,所以每一个线程都使用一个全局互斥体来保证同时只有一个线程能向它写入。

 

许多读者可能已经注意到List2中传递数据给线程还必须的手工写一个函数。尽管这个例子很简单,如果每一次都要写这样的代码实在是让人厌烦的事。别急,有一种简单的解决办法。函数库允许你通过将另一个函数绑定,并传入调用时需要的数据来创建一个新的函数。 List3向你展示了如何使用Boost.Bind库来简化List2中的代码,这样就不必手工写这些函数对象了。

例2:
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <iostream>

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;
}

例3: // 这个例子和例2一样,除了使用Boost.Bind来简化创建线程携带数据,避免使用函数对象
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <iostream>

boost::mutex io_mutex;

void count(int id)
{
        for (int i = 0; i < 10; ++i)
        {
                boost::mutex::scoped_lock
                lock(io_mutex);
                std::cout << id << ": " <<
                i << std::endl;
        }
}

int main(int argc, char* argv[])
{
        boost::thread thrd1(
        boost::bind(&count, 1));
        boost::thread thrd2(
        boost::bind(&count, 2));
        thrd1.join();
        thrd2.join();
        return 0;
}
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">3 条件变量
有的时候仅仅依靠锁住共享资源来使用它是不够的。有时候共享资源只有某些状态的时候才能够使用。比方说,某个线程如果要从堆栈中读取数据,那么如果栈中没有数据就必须等待数据被压栈。这种情况下的同步使用互斥体是不够的。另一种同步的方式--条件变量,就可以使用在这种情况下。

 

条件变量的使用总是和互斥体及共享资源联系在一起的。线程首先锁住互斥体,然后检验共享资源的状态是否处于可使用的状态。如果不是,那么线程就要等待条件变量。要指向这样的操作就必须在等待的时候将互斥体解锁,以便其他线程可以访问共享资源并改变其状态。它还得保证从等到得线程返回时互斥体是被上锁得。当另一个线程改变了共享资源的状态时,它就要通知正在等待条件变量得线程,并将之返回等待的线程。

 

List4是一个使用了boost::condition的简单例子。有一个实现了有界缓存区的类和一个固定大小的先进先出的容器。由于使用了互斥体boost::mutex,这个缓存区是线程安全的。put和get使用条件变量来保证线程等待完成操作所必须的状态。有两个线程被创建,一个在buffer中放入100个整数,另一个将它们从buffer中取出。这个有界的缓存一次只能存放10个整数,所以这两个线程必须周期性的等待另一个线程。为了验证这一点,put和get在std::cout中输出诊断语句。最后,当两个线程结束后,main函数也就执行完毕了。

 
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <iostream>

const int BUF_SIZE = 10;
const int ITERS = 100;

boost::mutex io_mutex;

class buffer
{
        public:
        typedef boost::mutex::scoped_lock
        scoped_lock;
        
        buffer()
        : p(0), c(0), full(0)
        {
        }
        
        void put(int m)
        {
                scoped_lock lock(mutex);
                if (full == BUF_SIZE)
                {
                        {
                                boost::mutex::scoped_lock
                                lock(io_mutex);
                                std::cout <<
                                "Buffer is full. Waiting..."
                                << std::endl;
                        }
                        while (full == BUF_SIZE)
                        cond.wait(lock);
                }
                buf[p] = m;
                p = (p+1) % BUF_SIZE;
                ++full;
                cond.notify_one();
        }
        
        int get()
        {
                scoped_lock lk(mutex);
                if (full == 0)
                {
                        {
                                boost::mutex::scoped_lock
                                lock(io_mutex);
                                std::cout <<
                                "Buffer is empty. Waiting..."
                                << std::endl;
                        }
                        while (full == 0)
                        cond.wait(lk);
                }
                int i = buf[c];
                c = (c+1) % BUF_SIZE;
                --full;
                cond.notify_one();
                return i;
        }
        
        private:
        boost::mutex mutex;
        boost::condition cond;
        unsigned int p, c, full;
        int buf[BUF_SIZE];
};

buffer buf;

void writer()
{
        for (int n = 0; n < ITERS; ++n)
        {
                {
                        boost::mutex::scoped_lock
                        lock(io_mutex);
                        std::cout << "sending: "
                        << n << std::endl;
                }
                buf.put(n);
        }
}

void reader()
{
        for (int x = 0; x < ITERS; ++x)
        {
                int n = buf.get();
                {
                        boost::mutex::scoped_lock
                        lock(io_mutex);
                        std::cout << "received: "
                        << n << std::endl;
                }
        }
}

int main(int argc, char* argv[])
{
        boost::thread thrd1(&reader);
        boost::thread thrd2(&writer);
        thrd1.join();
        thrd2.join();
        return 0;
}

 
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">4 线程局部存储
大多数函数都不是可重入的。这也就是说在某一个线程已经调用了一个函数时,如果你再调用同一个函数,那么这样是不安全的。一个不可重入的函数通过连续的调用来保存静态变量或者是返回一个指向静态数据的指针。 举例来说,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。

 

例5:
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/tss.hpp>
#include <iostream>

boost::mutex io_mutex;
boost::thread_specific_ptr<int> ptr;

struct count
{
        count(int id) : id(id) { }
        
        void operator()()
        {
                if (ptr.get() == 0)
                ptr.reset(new int(0));
                
                for (int i = 0; i < 10; ++i)
                {
                        (*ptr)++;
                        boost::mutex::scoped_lock
                        lock(io_mutex);
                        std::cout << id << ": "
                        << *ptr << 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;
}
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">5 仅运行一次的例程
还有一个问题没有解决:如何使得初始化工作(比如说构造函数)也是线程安全的。比方说,如果一个引用程序要产生唯一的全局的对象,由于实例化顺序的问题,某个函数会被调用来返回一个静态的对象,它必须保证第一次被调用时就产生这个静态的对象。这里的问题就是如果多个线程同时调用了这个函数,那么这个静态对象的构造函数就会被调用多次,这样错误产生了。

 

解决这个问题的方法就是所谓的“一次实现”(once routine)。“一次实现”在一个应用程序只能执行一次。如果多个线程想同时执行这个操作,那么真正执行的只有一个,而其他线程必须等这个操作结束。为了保证它只被执行一次,这个routine由另一个函数间接的调用,而这个函数传给它一个指针以及一个标志着这个routine是否已经被调用的特殊标志。这个标志是以静态的方式初始化的,这也就保证了它在编译期间就被初始化而不是运行时。因此也就没有多个线程同时将它初始化的问题了。Boost线程库提供了boost::call_once来支持“一次实现”,并且定义了一个标志boost::once_flag及一个初始化这个标志的宏BOOST_ONCE_INIT。

 

List6是一个使用了boost::call_once的例子。其中定义了一个静态的全局整数,初始值为0;还有一个由BOOST_ONCE_INIT初始化的静态boost::once_flag实例。main函数创建了两个线程,它们都想通过传入一个函数调用boost::call_once来初始化这个全局的整数,这个函数是将它加1。main函数等待着两个线程结束,并将最后的结果输出的到std::cout。由最后的结果可以看出这个操作确实只被执行了一次,因为它的值是1。

 
#include <boost/thread/thread.hpp>
#include <boost/thread/once.hpp>
#include <iostream>

int i = 0;
boost::once_flag flag =
BOOST_ONCE_INIT;

void init()
{
        ++i;
}

void thread()
{
        boost::call_once(&init, flag);
}

int main(int argc, char* argv[])
{
        boost::thread thrd1(&thread);
        boost::thread thrd2(&thread);
        thrd1.join();
        thrd2.join();
        std::cout << i << std::endl;
        return 0;
}
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">6 Boost线程库的未来
Boost线程库正在计划加入一些新特性。其中包括boost::read_write_mutex,它可以让多个线程同时从共享区中读取数据,但是一次只可能有一个线程向共享区写入数据;boost::thread_barrier,它使得一组线程处于等待状态,知道所有得线程都都进入了屏障区;boost::thread_pool,他允许执行一些小的routine而不必每一都要创建或是销毁一个线程。

 

Boost线程库已经作为标准中的类库技术报告中的附件提交给C++标准委员会,它的出现也为下一版C++标准吹响了第一声号角。委员会成员对Boost线程库的初稿给予了很高的评价,当然他们还会考虑其他的多线程库。他们对在C++标准中加入对多线程的支持非常感兴趣。从这一点上也可以看出,多线程在C++中的前途一片光明。

EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE

TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT

BOOST 线程完全攻略

1 创建线程

 首先看看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结合起来使用。

 

2 互斥体

  任何写过多线程程序的人都知道避免不同线程同时访问共享区域的重要性。如果一个线程要改变共享区域中某个数据,而与此同时另一线程正在读这个数据,那么结果将是未定义的。为了避免这种情况的发生就要使用一些特殊的原始类型和操作。其中最基本的就是互斥体(mutex,mutual exclusion的缩写)。一个互斥体一次只允许一个线程访问共享区。当一个线程想要访问共享区时,首先要做的就是锁住(lock)互斥体。如果其他的线程已经锁住了互斥体,那么就必须先等那个线程将互斥体解锁,这样就保证了同一时刻只有一个线程能访问共享区域。

 

  互斥体的概念有不少变种。Boost线程库支持两大类互斥体,包括简单互斥体(simple mutex)和递归互斥体(recursive mutex)。如果同一个线程对互斥体上了两次锁,就会发生死锁(deadlock),也就是说所有的等待解锁的线程将一直等下去。有了递归互斥体,单个线程就可以对互斥体多次上锁,当然也必须解锁同样次数来保证其他线程可以对这个互斥体上锁。

 

 

 

在这两大类互斥体中,对于线程如何上锁还有多个变种。一个线程可以有三种方法来对一个互斥体加锁:

 

  1. 一直等到没有其他线程对互斥体加锁。
  2. 如果有其他互斥体已经对互斥体加锁就立即返回。
  3. 一直等到没有其他线程互斥体加锁,直到超时。

 

似乎最佳的互斥体类型是递归互斥体,它可以使用所有三种上锁形式。然而每一个变种都是有代价的。所以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++保证了析构函数一定会被调用,所以即使是有异常抛出,互斥体也总是会被正确的解锁。

3 、条件变量

  有的时候仅仅依靠锁住共享资源来使用它是不够的。有时候共享资源只有某些状态的时候才能够使用。比方说,某个线程如果要从堆栈中读取数据,那么如果栈中没有数据就必须等待数据被压栈。这种情况下的同步使用互斥体是不够的。另一种同步的方式--条件变量,就可以使用在这种情况下。

  条件变量的使用总是和互斥体及共享资源联系在一起的。线程首先锁住互斥体,然后检验共享资源的状态是否处于可使用的状态。如果不是,那么线程就要等待条件变量。要指向这样的操作就必须在等待的时候将互斥体解锁,以便其他线程可以访问共享资源并改变其状态。它还得保证从等到得线程返回时互斥体是被上锁得。当另一个线程改变了共享资源的状态时,它就要通知正在等待条件变量得线程,并将之返回等待的线程。

  List4是一个使用了boost::condition的简单例子。有一个实现了有界缓存区的类和一个固定大小的先进先出的容器。由于使用了互斥体boost::mutex,这个缓存区是线程安全的。put和get使用条件变量来保证线程等待完成操作所必须的状态。有两个线程被创建,一个在buffer中放入100个整数,另一个将它们从buffer中取出。这个有界的缓存一次只能存放10个整数,所以这两个线程必须周期性的等待另一个线程。为了验证这一点,put和get在std::cout中输出诊断语句。最后,当两个线程结束后,main函数也就执行完毕了。

 

 4、线程局部存储

  大多数函数都不是可重入的。这也就是说在某一个线程已经调用了一个函数时,如果你再调用同一个函数,那么这样是不安全的。一个不可重入的函数通过连续的调用来保存静态变量或者是返回一个指向静态数据的指针。 举例来说,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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值