muduo網路庫學習之ThreadLocal 類、ThreadLocalSingleton類封裝知識點
一、ThreadLocal<T>類
1、在單執行緒程式中,我們經常要用到"全域性變數"以實現多個函式間共享資料。
2、在多執行緒環境下,由於資料空間是共享的,因此全域性變數也為所有執行緒所共有。
3、但有時應用程式設計中有必要提供執行緒私有的全域性變數,僅在某個執行緒中有效,但卻可以跨多個函式訪問。
4、POSIX執行緒庫通過維護一定的資料結構來解決這個問題,這些資料稱為(Thread-specific Data,或 TSD)。
5、執行緒特定資料也稱為執行緒本地儲存TLS(Thread-local storage)
6、對於POD型別的執行緒本地儲存,可以用__thread關鍵字
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
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); } |
而不同執行緒呼叫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$
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_);
}
{
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
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++網路庫》
相關文章
- muduo網路庫學習之Timestamp類、AtomicIntegerT 類封裝中的知識點封裝
- muduo網路庫學習之MutexLock類、MutexLockGuard類、Condition類、CountDownLatch類封裝中的知識點MutexCountDownLatch封裝
- muduo網路庫學習之BlockinngQueue類、ThreadPool 類、Singleton類封裝中的知識點BloCthread封裝
- muduo網路庫學習之Logger類、LogStream類、LogFile類封裝中的知識點封裝
- muduo網路庫學習之Exception類、Thread 類封裝中的知識點(重點講pthread_atfork())Exceptionthread封裝
- muduo網路庫學習筆記(8):高效日誌類的封裝筆記封裝
- muduo網路庫學習之muduo_http 庫涉及到的類HTTP
- muduo網路庫學習之EventLoop(四):EventLoopThread 類、EventLoopThreadPool 類OOPthread
- muduo網路庫學習之muduo_inspect 庫涉及到的類
- muduo網路庫學習筆記(1):Timestamp類筆記
- muduo網路庫學習筆記(3):Thread類筆記thread
- muduo網路庫Timestamp類
- muduo網路庫Exception異常類Exception
- muduo網路庫學習筆記(12):TcpServer和TcpConnection類筆記TCPServer
- muduo網路庫學習之EventLoop(一):事件迴圈類圖簡介和muduo 定時器TimeQueueOOP事件定時器
- 13封裝網路請求類庫封裝
- muduo網路庫AtomicIntegerT原子整數類
- muduo網路庫學習筆記(6):單例類(執行緒安全的)筆記單例執行緒
- muduo網路庫學習之EventLoop(七):TcpClient、ConnectorOOPTCPclient
- 靜態庫封裝之ComStr類封裝
- 靜態庫封裝之ComFile類封裝
- 靜態庫封裝之ComDir類封裝
- Laravel 小知識點之 HtmlString 類LaravelHTML
- muduo網路庫編譯安裝編譯
- JavaSE基礎知識學習-----Object類JavaObject
- muduo網路庫學習筆記(2):原子性操作筆記
- 學習網路安全能掌握哪些知識點?網路安全技術學習
- muduo網路庫學習之EventLoop(六):TcpConnection::send()、shutdown()、handleRead()、handleWrite()OOPTCP
- jquery學習之重要知識點jQuery
- muduo網路庫學習筆記(14):chargen服務示例筆記
- java學習 -- 利用類的反射和泛型自己動手寫jdbc封裝小類庫Java反射泛型JDBC封裝
- java學習之基本包裝類Java
- JavaSE基礎知識學習—–抽象類和介面Java抽象
- JavaSE基礎知識學習-----抽象類和介面Java抽象
- muduo網路庫學習筆記(11):有用的runInLoop()函式筆記OOP函式
- Retrofit+okhttp+Rxjava封裝網路請求工具類HTTPRxJava封裝
- Android之Activity基類封裝Android封裝
- 物件與類_知識點筆記物件筆記