muduo網路庫學習之ThreadLocal 類、ThreadLocalSingleton類封裝知識點

s1mba發表於2013-11-01
一、ThreadLocal<T>類

1、在單執行緒程式中,我們經常要用到"全域性變數"以實現多個函式間共享資料。

2、在多執行緒環境下,由於資料空間是共享的,因此全域性變數也為所有執行緒所共有。

3、但有時應用程式設計中有必要提供執行緒私有的全域性變數,僅在某個執行緒中有效,但卻可以跨多個函式訪問。

4、POSIX執行緒庫通過維護一定的資料結構來解決這個問題,這些資料稱為(Thread-specific Data,或 TSD)。

5、執行緒特定資料也稱為執行緒本地儲存TLS(Thread-local storage)

6、對於POD型別的執行緒本地儲存,可以用__thread關鍵字


pthread_key_create、pthread_key_delete、pthread_getspecific、pthread_setspecific,更多資訊請看這裡

template<typename T>
class ThreadLocal : boost::noncopyable

一旦某個執行緒建立了一個key,比如key[1],那麼其他執行緒也有自己的key[1],它們通過各自的key[1]訪問到的實際資料(堆上記憶體分配的空間)是不同的,pthread_key_delete 只是刪除key,實際資料空間的釋放需要在pthread_key_create 中註冊一個回撥函式destructor去delete T*。

ThreadLocal()
  {
    pthread_key_create(&pkey_, &ThreadLocal::destructor);
  }

當某個執行緒執行結束,這個執行緒對應的實際資料T也沒有存在的價值,會呼叫destructor,從而delete T*;

也就是說如果有兩個執行緒,那麼destructor會執行兩次,因為實際資料T在每個執行緒中各有一份。

key 的刪除在~ThreadLocal()函式內:

~ThreadLocal()
 {
    pthread_key_delete(pkey_);
 }

測試程式碼:
SingletonThreadLocal_test.cc:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
 
#include <muduo/base/Singleton.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/ThreadLocal.h>
#include <muduo/base/Thread.h>

#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include <stdio.h>

class Test : boost::noncopyable
{
public:
    Test()
    {
        printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
    }

    ~Test()
    {
        printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
    }

    const std::string &name() const
    {
        return name_;
    }
    void setName(const std::string &n)
    {
        name_ = n;
    }

private:
    std::string name_;
};

#define STL muduo::Singleton<muduo::ThreadLocal<Test> >::instance().value()

void print()
{
    printf("tid=%d, %p name=%s\n",
           muduo::CurrentThread::tid(),
           &STL,
           STL.name().c_str());
}

void threadFunc(const char *changeTo)
{
    print();
    STL.setName(changeTo);
    sleep(1);
    print();
}

int main()
{
    STL.setName("main one");
    muduo::Thread t1(boost::bind(threadFunc, "thread1"));
    muduo::Thread t2(boost::bind(threadFunc, "thread2"));
    t1.start();
    t2.start();
    t1.join();
    print();
    t2.join();
    pthread_exit(0);
}

muduo::Singleton<muduo::ThreadLocal<Test> >::instance() 保證不同執行緒呼叫返回的都是同一個ThreadLocal<Test> 物件,
而不同執行緒呼叫ThreadLocal<Test>.value(); 返回的是不同的Test物件,即在不同執行緒中各有一份實際資料。

輸出如下:
// 紅色為不同Test物件的地址
simba@ubuntu:~/Documents/build/debug/bin$ ./singletonthreadlocal_test 
tid=2894, constructing 0x8507038 //main thread
tid=2896, constructing 0xb6200468 // thread2
tid=2896, 0xb6200468 name=
tid=2895, constructing 0xb6300468 // thread1
tid=2895, 0xb6300468 name=
tid=2896, 0xb6200468 name=thread2
tid=2896, destructing 0xb6200468 thread2
tid=2895, 0xb6300468 name=thread1
tid=2895, destructing 0xb6300468 thread1
tid=2894, 0x8507038 name=main one
tid=2894, destructing 0x8507038 main one
simba@ubuntu:~/Documents/build/debug/bin$ 


二、ThreadLocalSingleton<T>類

template<typename T>
class ThreadLocalSingleton : boost::noncopyable
{
     class Deleter { };
};

每個執行緒都有一個單例物件T,即每個執行緒都有一個T*(__thread修飾,指標是POD型別資料),每個執行緒都有各自的特定資料(用TSD實現),t_value_ 即指向實際資料T的指標。

其中instance() 的實現與Singleton 類的實現不同,因為這裡是每個執行緒各有一個單例物件T,而不是所有執行緒共享一個。

deleter_ 是靜態資料成員,為所有執行緒所共享;t_value_ 雖然也是靜態資料成員,但加了__thread 修飾符,故每一個執行緒都會有一份。Deleter類是用來實現當某個執行緒執行完畢,執行註冊的destructor函式,進而delete t_value_ 。key 的刪除在~Deleter() 中

~Deleter()
{
      pthread_key_delete(pkey_);
}

測試程式碼:
ThreadLocalSingleton_test.cc:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <muduo/base/ThreadLocalSingleton.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Thread.h>

#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include <stdio.h>

class Test : boost::noncopyable
{
public:
    Test()
    {
        printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
    }

    ~Test()
    {
        printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
    }

    const std::string &name() const
    {
        return name_;
    }
    void setName(const std::string &n)
    {
        name_ = n;
    }

private:
    std::string name_;
};

void threadFunc(const char *changeTo)
{
    printf("tid=%d, %p name=%s\n",
           muduo::CurrentThread::tid(),
           &muduo::ThreadLocalSingleton<Test>::instance(),
           muduo::ThreadLocalSingleton<Test>::instance().name().c_str());
    muduo::ThreadLocalSingleton<Test>::instance().setName(changeTo);
    sleep(2);  //如果沒有sleep,可能thread2在thread1退出後才執行,%p可能是原來的值
    printf("tid=%d, %p name=%s\n",
           muduo::CurrentThread::tid(),
           &muduo::ThreadLocalSingleton<Test>::instance(),
           muduo::ThreadLocalSingleton<Test>::instance().name().c_str());

    // no need to manually delete it
    // muduo::ThreadLocalSingleton<Test>::destroy();
}

int main()
{
    muduo::ThreadLocalSingleton<Test>::instance().setName("main one");
    muduo::Thread t1(boost::bind(threadFunc, "thread1"));
    muduo::Thread t2(boost::bind(threadFunc, "thread2"));
    t1.start();
    t2.start();
    t1.join();
    printf("tid=%d, %p name=%s\n",
           muduo::CurrentThread::tid(),
           &muduo::ThreadLocalSingleton<Test>::instance(),
           muduo::ThreadLocalSingleton<Test>::instance().name().c_str());
    t2.join();

    pthread_exit(0);
}

輸出如下:
// 紅色為不同Test 物件的地址
simba@ubuntu:~/Documents/build/debug/bin$ ./threadlocalsingleton_test 
tid=3341, constructing 0x8a22028  // main thread
tid=3343, constructing 0xb6200468  // thread 2
tid=3343, 0xb6200468 name=
tid=3342, constructing 0xb6000468  // thread 1
tid=3342, 0xb6000468 name=
tid=3343, 0xb6200468 name=thread2
tid=3343, destructing 0xb6200468 thread2
tid=3342, 0xb6000468 name=thread1
tid=3342, destructing 0xb6000468 thread1
tid=3341, 0x8a22028 name=main one
tid=3341, destructing 0x8a22028 main one


參考:
muduo manual.pdf
《linux 多執行緒伺服器程式設計:使用muduo c++網路庫》

相關文章