無論如何,要編寫一個多執行緒安全(thread-safe)的程式總是困難的,為了使用的共用資源,您必須小心 的對共用資源進行同步,同步帶來一定的效 能延遲,而另一方面,在處理同步的時候,又要注意物件的鎖定與釋放,避免產生死結,種種因素都使得編寫多執行緒程式變得困難。
Thread-Specific Storage模式嘗試從另一個角度來解釋多執行緒共用資源的問題,其思考點很簡單,即然共用資源這麼困難,那麼就乾脆不要共用,何不為每個執行緒創造一 個資源的複本,將每一個執行緒存取資料的行為加以隔離,其實現的方法,就是給予每一個執行緒一個特定空間來保管該執行緒所獨享的資源,也因此而稱之為 Thread- Specific Storage模式。
實作Thread-Specific Storage模式,最基本的方式,就是使用一個關聯容器物件,例如 關 聯容器(QMap、QHash...),在執行緒獲得個別資源時,使用QThread::currentThread()取得執行緒ID,將ID為鍵(Key)、資源為值(Value)存入關聯容器之中,要取得執行緒個別資源時,則以執行緒ID為鍵來取得相對應的資源。
下面這個簡單的MessageThreadLocal簡單實作了Thread-Specific Storage的概念:
class MessageThreadLocal {
public:
QString get();
void set(const QString &message);
private:
QMap<QThread*, QString> map;
};
QString MessageThreadLocal::get() {
QThread *thread = QThread::currentThread();
QString message = map.value(thread, "N.A.");
if(message == "N.A." && !map.contains(thread)) {
map.insert(thread, "N.A.");
}
return message;
}
void MessageThreadLocal::set(const QString &message) {
map.insert(QThread::currentThread(), message);
}
public:
QString get();
void set(const QString &message);
private:
QMap<QThread*, QString> map;
};
QString MessageThreadLocal::get() {
QThread *thread = QThread::currentThread();
QString message = map.value(thread, "N.A.");
if(message == "N.A." && !map.contains(thread)) {
map.insert(thread, "N.A.");
}
return message;
}
void MessageThreadLocal::set(const QString &message) {
map.insert(QThread::currentThread(), message);
}
在Qt 中,您不用親自實作這樣的ThreadLocal類別,它提供有QThreadStorage類別,可以讓您直接用來實現Thread-Specific Storage模式,例如API文件中QThreadStorage的說明中,提供一個簡單的範例片段,示範如何為每個執行緒儲存一個快取物件:
QThreadStorage<QCache<QString, SomeClass> *> caches;
void cacheObject(const QString &key, SomeClass *object) {
if (!caches.hasLocalData())
caches.setLocalData(new QCache<QString, SomeClass>);
caches.localData()->insert(key, object);
}
void removeFromCache(const QString &key) {
if (!caches.hasLocalData())
return;
caches.localData()->remove(key);
}
void cacheObject(const QString &key, SomeClass *object) {
if (!caches.hasLocalData())
caches.setLocalData(new QCache<QString, SomeClass>);
caches.localData()->insert(key, object);
}
void removeFromCache(const QString &key) {
if (!caches.hasLocalData())
return;
caches.localData()->remove(key);
}
使用QThreadStorage時要注意的是,由於某些編譯器的限制,QThreadStorage只能儲存指標。