和许多客户端程序一样,chromium中同样使用到了多线程,每个线程可能包括一个消息循环MessageLoop,它通过调用current函数返回,因为MessageLoop是线程的私有数据,线程内共享而线程间不共享,这就使用到了线程局部存储(Thread Local Storage,TLS)技术。TLS技术在不同的操作系统中通过不同的系统调用实现,它为线程提供一种保存私有数据的方法,使线程的私有数据对其它线程不可见。chromium通过定义模版类ThreadLocalPointer实现线程私有的数据指针。以下是ThreadLocalPointer的实现
template <typename Type>
class ThreadLocalPointer {
public:
ThreadLocalPointer() : slot_() {
internal::ThreadLocalPlatform::AllocateSlot(&slot_);
}
~ThreadLocalPointer() {
internal::ThreadLocalPlatform::FreeSlot(slot_);
}
Type* Get() {
return static_cast<Type*>(
internal::ThreadLocalPlatform::GetValueFromSlot(slot_));
}
void Set(Type* ptr) {
internal::ThreadLocalPlatform::SetValueInSlot(
slot_, const_cast<void*>(static_cast<const void*>(ptr)));
}
private:
typedef internal::ThreadLocalPlatform::SlotType SlotType;
SlotType slot_;
DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>);
};
对于不同的操作系统,TLS的实现方式并不相同,所以在实现过程中定久了接口类ThreadLocalPlatform,它定义了TLS的一些基本操作,对于不同的操作系统,只需要对接口进行实现就可以了。例如,在windows下的实现为:
void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
*slot = TlsAlloc();
CHECK_NE(*slot, TLS_OUT_OF_INDEXES);
}
// static
void ThreadLocalPlatform::FreeSlot(SlotType slot) {
if (!TlsFree(slot)) {
NOTREACHED() << "Failed to deallocate tls slot with TlsFree().";
}
}
// static
void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
return TlsGetValue(slot);
}
// static
void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
if (!TlsSetValue(slot, value)) {
LOG(FATAL) << "Failed to TlsSetValue().";
}
}
在实现中可以看出,当 ThreadLocalPointer构造的时候,由操作系统分配出一个slot_,通过slot_可以取出保存和读取特定的数据,在 ThreadLocalPointer中,这个数据的类型就是一个指针。
这样说明还是很不容易理解TLS的原理,其实可以理解成操作系统为每一个线程都分配了一个大小相同的数组TLS_ARRAY,当调用TlsAlloc时,系统遍历所有的线程,找到一个slot_,在所有线程中TLS_ARRAY[slot_]都为空,slot_值就相当于一个索引保存下来。当某一个线程要设置私有数据时,操作系统会根据slot_值找到线程私有的数据TLS_ARRAY[slot_],用户可以设置它的值或者调取它的值。
当ThreadLocalPointer析构的时候,会调用TlsFree释放slot_,这是所有数据中TLS_ARRAY[slot_]的值都置为了空。