大卫的思维空间

自由的国度,思维的空间......
随笔 - 67, 文章 - 3, 评论 - 117, 引用 - 11

导航

<2009年1月>
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567

公告

大卫语:
我不是天生的王者,除了奋斗,我别无选择!
David's Motto:
Since I am not an inborn victor, I have no choice but strive!

留言簿(6)

随笔分类

随笔档案

文章分类

文章档案

相册

相关链接

搜索

最新评论

阅读排行榜

评论排行榜

boost::thread简要分析(3):线程局部存储及其它

多线程编程中还有一个重要的概念:Thread Local Store(TLS,线程局部存储),在boost中,TLS也被称作TSS,Thread Specific Storage。
boost::thread库为我们提供了一个接口简单的TLS的面向对象的封装,以下是tss类的接口定义:
class
 tss
{

public
:
    tss(boost::function1<void, void*>* pcleanup);
    void
* get() const;
    void
 set(void* value);
    void
 cleanup(void* p);
};

分别用于获取、设置、清除线程局部存储变量,这些函数在内部封装了TlsAlloc、TlsGetValue、TlsSetValue等API操作,将它们封装成了OO的形式。
但boost将该类信息封装在detail名字空间内,即不推荐我们使用,当需要使用tss时,我们应该使用另一个使用更加方便的类:thread_specific_ptr,这是一个智能指针类,该类的接口如下:
class
 thread_specific_ptr : private boost::noncopyable   // Exposition only
{
public
:
  // construct/copy/destruct
  thread_specific_ptr();
  thread_specific_ptr(void (*cleanup)(void*));
  ~
thread_specific_ptr();

  // modifier functions
  T* release();
  void
 reset(T* = 0);

  // observer functions
  T* get() const;
  T* operator->() const;
  T& operator*()() const;
};

即可支持get、reset、release等操作。
thread_specific_ptr类的实现十分简单,仅仅为了将tss类“改装”成智能指针的样子,该类在其构造函数中会自动创建一个tss对象,而在其析构函数中会调用默认参数的reset函数,从而引起内部被封装的tss对象被析构,达到“自动”管理内存分配释放的目的。

以下是一个运用thread_specific_ptr实现TSS的例子:
#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;    // use this method to tell that this member will not shared by all threads

struct
 count
{

    count(int id) : id(id) { }

    void
 operator()()
    {

        if
 (ptr.get() == 0)    // if ptr is not initialized, initialize it
            ptr.reset(new int(0));    // Attention, we pass a pointer to reset (actually set ptr)

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


此外,thread库还提供了一个很有趣的函数,call_once,在tss::init的实现中就用到了该函数。
该函数的声明如下:
void
 call_once(void (*func)(), once_flag& flag);
该函数的Windows实现通过创建一个Mutex使所有的线程在尝试执行该函数时处于等待状态,直到有一个线程执行完了func函数,该函数的第二个参数表示函数func是否已被执行,该参数往往被初始化成BOOST_ONCE_INIT(即0),如果你将该参数初始化成1,则函数func将不被调用,此时call_once相当于什么也没干,这在有时候可能是需要的,比如,根据程序处理的结果决定是否需要call_once某函数func。
call_once在执行完函数func后,会将flag修改为1,这样会导致以后执行call_once的线程(包括等待在Mutex处的线程和刚刚进入call_once的线程)都会跳过执行func的代码。

需要注意的是,该函数不是一个模板函数,而是一个普通函数,它的第一个参数1是一个函数指针,其类型为void (*)(),而不是跟boost库的很多其它地方一样用的是function模板,不过这样也没有关系,有了boost::bind这个超级武器,想怎么绑定参数就随你的便了,根据boost的文档,要求传入的函数不能抛出异常,但从实现代码中好像不是这样。

以下是一个典型的运用call_once实现一次初始化的例子:
#include <boost/thread/thread.hpp>
#include <boost/thread/once.hpp>
#include <iostream>

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

void
 init()
{
    ++
i;
}


void
 thread()
{

    boost::call_once(&init, flag);
    ++
j;
}


int
 main(int argc, char* argv[])
{

    boost::thread thrd1(&thread);
    boost::thread thrd2(&thread);
    thrd1.join();
    thrd2.join();

    std::cout << i << std::endl;
    std::cout << j << std::endl;

    return
 0;
}

结果显示,全局变量i仅被执行了一次++操作,而变量j则在两个线程中均执行了++操作。

其它
boost::thread目前还不十分完善,最主要的问题包括:没有线程优先级支持,或支持线程的取消操作等,而且,目前的实现机制似乎不大容易通过简单修改达到这一要求,也许将来的某个版本会在实现上出现较大调整,但目前支持的接口应该会相对保持稳定,目前支持的特性也还会继续有效。

posted on 2005-05-24 12:52 大卫的思维空间 阅读(3537) 评论(13)  编辑 收藏

评论

# re: boost::thread简要分析(3):线程局部存储及其它

学习成本也比较高,还不如自己封装一个(当然,跨LINUX和NT平台)快呢。
2005-05-24 12:56 | Z

# re: boost::thread简要分析(3):线程局部存储及其它

boost的thread库相对来说已经是最"直白"的了. 学习难度也不高. 
2005-05-24 16:32 | A

# re: boost::thread简要分析(3):线程局部存储及其它

在WIN下的人很少会知道join是啥意思。
2005-05-24 21:09 | R

# to 楼上

关于join,我已经在第一篇中特别说明并分析其实现了。
必须承认这个函数的名字没有什么提示性,不容易让人想起是等待线程退出。
2005-05-25 16:46 | Bill David

# 幸好我不喜欢boost

连函数指针都还要别人给的设计,不知道还有什么实际意义。
而且,概念太多,人看了就烦,随还能学?
性能不好,概念多,学这些概念清楚,认真C++和平台API。
早就自己都比用他happy了。

也没看出他设计上的优势。恐怕不管是初学,还是应用,都没必要跟这个风。
2005-05-25 20:24 | 清风雨

# re: boost::thread简要分析(3):线程局部存储及其它

join 对windows下开发者来说可能是比较生疏, 但是对unix下来说又很自然, 总要选一种吧. 由于unix比windows早, 选join也不为过.

boost作为一个整体, thread使用其中的函数指针也应该可以接受. 不过确实有些boost类库太难看懂了, thread应该算是简单的了.

2005-05-26 10:12 | ilovevc

# to 清风雨

1、不可否认boost目前没有应用价值,但绝对有参考价值,我所知道的maillist中,boost是最热的,甚至超过了google的C++ maillist,这些人不会都是疯子。
2、目前的thread库封装形式有两种:Java通过实现接口或派生子类来实现,而其它则往往需要使用者提供一个回调函数以执行相关处理,相比之下,后者显得更加灵活。
3、你对OO的要求太高了,在应用线程库方面,以上任何一种形式都需要你告诉线程你希望它执行什么,难道你还有更好的设计?
4、OO中库能做的是将固化可以固化的东西,将变化的东西交给使用者去实现。回调函数是将变化的东西传递给库的最重要的形式之一,也许n年后,编程会比现在门槛低得多,甚至可以交给机器人来完成,但现在,我们能做的只有学习、忍受和习惯这种所谓的局限性。
5、C++之所以比Java门槛高,一个重要的原因是Java不仅是一门语言,它还提供了很多随JDK发行的应用库,C++却没有。boost之所以如此来势汹涌,正是因为它在推动C++的发展,使得下一版本的C++规范将可能包含应用库,使得我们可以免去很多实现应用库的重复劳动。
6、C++除了继承和建立在继承基础上的系列技术,还有一个重要部分,那就是GP,boost中到处可以看到一些高级GP技术的应用,从而造成理解起来十分困难,因为绝大多数程序员所掌握的那些template的知识根本就不足以理解这些技术,这使得研究boost的起点很高(我所分析的thread库是个例外,前面已经说过),但应用boost库的起点是很低的,以thread库为例,其接口已经足够简单了。
2005-05-26 10:54 | Bill David

# re: boost::thread简要分析(3):线程局部存储及其它

就最近的两个例子, 我觉得以后想要当C++程序员你不懂模板几乎很难使用外部的C++内库了.
第一: ACE的创始人Doug前几天在邮件中发布了一个声明, 他们不再继续提供免费的VC6支持, 以后需要在VC6中使用ACE只能通过商业公司,  因为VC6对模板支持太差. 虽然俺现在还是靠VC6吃饭, 但是我绝对支持, 否则到处都是宏技巧, ACE_Static_cast等等类似的东西, 不胜其烦.

第二. Effective C++出了第3版, 看目录,其中提到了很多template的知识以及boost. 而我记得More Effective C++中仍然说到模板成员函数是"未开垦的蛮荒大地" (特意看了一下, 中文版179页). 
当时先进的东西现在由于编译器的进步已经很普通了. C++程序员再拒绝使用template几乎就是处于"石器时代". 

2005-05-26 11:57 | ilovevc

# re: boost::thread简要分析(3):线程局部存储及其它

项目导向而不是技术导向。
好多吹的人是学院派的。

石器时代的东东对大部分的项目已经足够了。

boost最新版本估计也要出来了,其实在CVS上大部分东东都已经有了。学习可以,使用不太想。
sandbox现在可以不用关注,那东东还早着呢。
2005-05-26 12:34 | X

# re: boost::thread简要分析(3):线程局部存储及其它

只要使用STL就是使用template, 不使用template我觉得真是不太可能,  写cout<<"hello,world"都涉及到模板, 因此不见得大部分项目都足够.
2005-05-26 13:05 | ilovevc

# re: boost::thread简要分析(3):线程局部存储及其它

# to 清风雨
看看adobe,google用了多少boost你就不会大放厥词了
2005-07-22 12:02 | # to 清风雨

# re: boost::thread简要分析(3):线程局部存储及其它

不错,学习 ^_^
2005-07-25 10:47 | blueskyzsz

# re: 还是趋向不使用这个

今天学习你的设计模式,看的有人说我大放厥词,突然奇怪了,我说什么了。才想起来看看。

对于线程的使用,个人感觉,一般选用封装库,出于以下考虑:
1.跨平台使用;
2.对平台API不熟、不懂;
3.处理麻烦;
那么,也就是选用它,它必需有以上优点:
1. 能夸平台,这个boost没问题;
2.windows下CreateThread需要一个函数指针,这里也是要函数指针或防函数,一样的对于初学者还是不懂。也就是给了已会者一个选择,都会了,选这个个人觉得实际意义不大,还有性能损失。
3.都是函数指针,照样的使用麻烦。

也就是,个人觉得boost不是为应用而设计的,或者对初级应用者和高级应用者的考虑都不够。(初级易用,高级高效)觉得它在这里做的不够好。
必要时,设计上还是有所取舍比较好。所以,我倾向java的Thread和Runnable的设计。
2005-08-03 09:42 | 清风雨
标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]