[原创] 为什么需要TLS(Thread Local Storage)?

线程局部存储(TLS)是Win32提供的一种底层基础技术,用于将某些数据和一特定线程关联起来,即,这些数据为关联线程所独有(私有)。初看上去,有点郁闷了:既然每个线程都有自己的私有堆栈,那么还要整个TLS做劳什子?线程的私用数据全放堆栈里不就得了?...... 然而事情并不是那么简单的=) 下面举个具体的例子。

所谓堆栈,相当于是一个历史记录,里面存储的数据(函参、返回值、调用上下文)与函数调用顺序有着密不可分的联系。假设有某线程ThreadProc的伪码是这样写滴:

int a=100
 
CreateThread(ThreadProc,NULL); 
 
void ThreadProc(void* lpvoid) 
    print(a
++); 
    A();     
}
 
 
void A() 
    print(a
++); 
    B(); 
}
 
 
void B() 
    print(a
++); 
    C(); 
}
 
 
void C() 
    print(a
++); 
}

其调用链很清楚,ThreadProc->A->B->C然后再返回,调用链中的每个函数都引用了a并且修改了a的值,注意到,“全局变量”a是没有存储在线程堆栈中的,但是与此同时我们想让a成为线程所独有的数据,即,不期望发生同时多个ThreadProc线程的实例访问并修改同一个全局变量,导致运行结果不确定的情况(运行结果的不确定源于线程调度顺序、调度时被中断位置的不确定),该怎么办?用TLS。

慢,事情还没有完!隔壁的某位先生马上抢白我了(绝杀状):“上述解释还是无法令人满意!——这个程序我还可以这样写,照样不需要TLS!” 下面一起来看看这个无需TLS的程序是怎么写的:

int a=100
 
CreateThread(ThreadProc,
&a); 
 
void ThreadProc(void* lpvoid) 
    
int aa=*((int*)lpvoid); 
    
int* pa=&aa;     
 
    print((
*pa)++); 
    A(pa);     
}
 
 
void A(int* pa) 
    print((
*pa)++);     
    B(pa); 
}
 
 
void B(int* pa) 
    print((
*pa)++);     
    C(pa); 
}
 
 
void C(int* pa) 
    print((
*pa)++);     
}
  

汗....本来想偷点懒de.....^^ 没办法了,看来偶也得放绝杀了。是的,我承认隔壁的先生的确是善于思考,通过copy一份全局变量a的值到线程自己的私有局部变量aa、同时在调用每一个函数时都将aa的指针作为函参的方法,几乎是完全避免了TLS的使用,也就否定了TLS存在的意义。不过,(嘿嘿,准备逆转)难道大家不觉得这种方式太烦了吗?且不论仅仅只用到了一个线程全局量的时候就这样了,更不用说假设要用到十个八个全局量的时候场面有多壮观了;还有,调用链上每个函数都有一个函参指针指向堆栈copy,假设是十个八个全局量、调用链又有个十层八层那该浪费多少堆栈里面的空间啊.....另外,再补充一点,为了向下兼容,例如,现有的C标准库在Win32之前已经存在很长一段时间了,由于没有考虑多线程环境的影响,里面大量地使用了全局静态变量,C标准库不是由你我写的,当然也就没法修改了,如果在线程体里面要使用这些“危险”函数的话,你我该怎么办?不用标准库?会被累死的......所以C/C++标准库使用了TLS,所以TLS有其存在的价值。

隔壁的先生不啃声了。一句话,存在,总得有道理=)
posted @ 2004-12-10 16:57  neoragex2002  阅读(10540)  评论(9编辑  收藏  举报