一、概述
现在多核时代多线程开发越来越重要了,多线程相比于多进程有诸多优势(当然也有诸多劣势)。在早期C的库中,有许多函数是线程不安全的,因为内部用到了静态变量,比如:char *strtok(char *s, const char *delim); 该函数内部就有一个静态指针,如果多个线程同时调用此函数时,可能就会出现奇怪的结果,当然也不是我们所想要的,现在LINUX对此函数功能有一个线程安全版本的接口:char *strtok_r(char *s, const char *delim, char **ptrptr),这就避免了多个线程同时访问的冲突问题。其实,如果保持 strtok()/2 接口不变,同时还要保证线程安全,还有一个解决办法,那就是采用线程局部变量。
使用线程局部变量有两种使用方式,一个稍微麻烦些,一个比较简单,下面一一做个介绍(以LINUX为例)
二、线程局部变量的使用
比较麻烦些的使用方法用到的函数主要有三个:pthread_once(pthread_once_t*, void (*init_routine)(void)), pthread_key_create()/2, pthread_setspecific()/2, pthread_getspecific()/1,其中 pthread_once 可以保证在整个进程空间init_routine函数仅被调用一次(它解决了多线程环境中使得互斥量和初始化代码都仅被初始化一次的问题);pthread_key_create 的参数之一指一个析构函数指针,当某个线程终止时该析构函数将被调用,并用对于一个进程内的给定键,该函数只能被调用一次;pthread_sespecific 和 pthread_getspecific 用来存放和获取与一个键关联的值。例子如下:
pthread_key_t key;
pthread_once_t once = PTHREAD_ONCE_INIT;
static void destructor(void *ptr)
{
free(ptr);
}
void init_once(void)
{
pthread_key_create(&key, destructor);
}
static void *get_buf(void)
{
pthread_once(&once, init_once);
if ((ptr = pthread_getspecific(key)) == NULL) {
ptr = malloc(1024);
pthread_setspecific(key, ptr);
}
return (ptr);
}
static void *thread_fn(void *arg)
{
char *ptr = (char*) get_buf();
sprintf(ptr, "hello world");
printf(">>%s\n", ptr);
return (NULL);
}
void test(void)
{
int i, n = 10;
pthread_t tids[10];
for (i = 0; i < n; i++) {
pthread_create(&tids[i], NULL, thread_fn, NULL);
}
for (i = 0; i < n; i++) {
pthread_join(&tids[i], NULL);
}
}
另外,还有一个更加简单使用线程局部变量的方法:__thread 修饰符, (在WIN32平台下需要用: __declspec(thread) 修饰符,WIN32的东东总得要多写几笔,呵呵),于是上述代码可以修改如下:
static void *get_buf(void)
{
static __thread void *ptr = malloc(1024);
return (ptr);
}
static void *thread_fn(void *arg)
{
char *ptr = (char*) get_buf();
sprintf(ptr, "hello world");
printf(">>%s\n", ptr);
return (NULL);
}
void test(void)
{
int i, n = 10;
pthread_t tids[10];
for (i = 0; i < n; i++) {
pthread_create(&tids[i], NULL, thread_fn, NULL);
}
for (i = 0; i < n; i++) {
pthread_join(&tids[i], NULL);
}
}
看到没有,这段代码比前面一个简单许多,但却有一个问题,它存在内存泄露问题,因为当线程退出时各个线程分配的动态内存(ptr = malloc(1024)) 并没有被释放。
三、用ACL线程接口操作线程局部变量
为了解决上述问题,ACL库中实现了线程局部变量的简单释放功能:acl_pthread_atexit_add(void *arg, void (*free_callback)(void*)),修改上述代码如下:
static void free_fn(void *ptr)
{
free(ptr);
}
static void *get_buf(void)
{
static __thread void *ptr = malloc(1024);
acl_pthread_atexit_add(ptr, free_fn);
return (ptr);
}
static void *thread_fn(void *arg)
{
char *ptr = (char*) get_buf();
sprintf(ptr, "hello world");
printf(">>%s\n", ptr);
return (NULL);
}
void test(void)
{
int i, n = 10;
pthread_t tids[10];
for (i = 0; i < n; i++) {
acl_pthread_create(&tids[i], NULL, thread_fn, NULL);
}
for (i = 0; i < n; i++) {
acl_pthread_join(&tids[i], NULL);
}
}
ok, 一切问题得到解决。细心的读者会发现 pthread_create, pthread_join 前面都加了前缀: acl_, 这是因为 ACL库对线程库进行了封装,以适应不同平台下(UNIX、WIN32)下的使用,这个例子是跨平台的,WIN32下同样可用。
个人微博:http://weibo.com/zsxxsz
分享到:
相关推荐
总之,局部变量线程安全测试是一项重要的软件质量保证措施,特别是在开发多线程应用时。通过编写和执行这样的测试,开发者可以确保他们的代码在并发环境下能够正确地工作,避免潜在的数据不一致性和其他线程安全问题...
在实际开发中,合理使用线程局部变量可以提高程序的并发性能,减少不必要的同步开销。但是,过度依赖线程局部变量可能会导致内存泄漏,因为这些变量不会自动被垃圾收集器清理。当线程结束时,如果未显式调用`...
数据竞争是当两个或更多线程同时修改同一全局变量时产生的,这可能导致不可预测的结果。为避免这种情况,我们需要同步机制,如互斥量(mutex)、临界区(critical section)或信号量(semaphore),来确保一次只有一...
在实际开发中,还需要关注线程局部存储(TLS,Thread Local Storage),它为每个线程提供独立的变量副本,避免了多线程共享数据时的同步问题。VC++提供了`_declspec(thread)`关键字或者`TlsAlloc/TlsFree`等API来...
在多线程环境下,局部变量更安全,因为它们不会被其他线程访问。 在C语言单片机编程中,由于资源有限,合理使用全局变量和局部变量可以优化内存管理。全局变量可以用于保存那些在整个程序运行过程中都需要保持的...
在IT行业中,多线程开发是一项至关重要的技术,特别是在当今的高性能计算和实时系统中。多线程允许程序同时执行多个任务,提高了系统的资源利用率和响应速度。本资源"多线程开发及其源代码"专注于教授如何进行多线程...
9. **线程局部存储**:`ThreadLocal<T>`类允许在线程中创建局部变量,每个线程拥有自己的副本,互不影响。 10. **线程优先级**:虽然可以设置线程优先级,但不推荐频繁使用,因为操作系统调度策略可能使得高优先级...
在实际应用中,可能还需要考虑线程局部存储(thread_local)、条件变量(condition_variable)等高级同步原语,以优化多线程间的通信和协作。此外,设计良好的并发代码应该尽量减少对全局状态的依赖,以提高可读性...
"鱼刺多线程注册源码例子"是一个基于"鱼刺多线程稳定框架"的编程实践,旨在展示如何在软件开发中有效地利用多线程技术来提高程序的执行效率和稳定性。在这个例子中,"鱼刺框架"可能是一个专门为多线程编程设计的开源...
【标题】"VS2015多线程" 涉及的知识点主要集中在C++编程语言中的多线程技术,以及如何在Visual Studio 2015开发环境中使用这些功能。多线程是现代软件开发中一个重要的概念,它允许程序同时执行多个任务,从而提高...
此外,由于VB6本身不支持线程局部存储,因此在设计多线程程序时,要格外注意数据的隔离和同步。 通过学习和实践这两个示例,开发者可以深入理解VB6的多线程机制,提高程序的性能和用户体验。不过,由于VB6已不再...
7. **线程局部存储**:如果需要每个线程拥有独立的数据,可以使用`std::thread_local`关键字。这样,每个线程都有自己的一份变量副本。 8. **线程池**:在实际应用中,为了避免频繁创建和销毁线程的开销,可以使用...
在这个“使用多线程进行文件搜索”的主题中,我们将深入探讨如何利用多线程来优化文件搜索操作,特别是在VC++6.0环境下。 1. **多线程的基本概念** - 线程是操作系统中的一个执行单元,每个线程都拥有自己的执行上...
只读段包含程序代码和只读数据,数据段存放已初始化的全局变量和静态变量,堆栈存放函数的参数值、局部变量值、返回地址,堆存放动态分配的数据,共享库的内存映射区域存放共享库的代码。 4. 线程的创建、挂起和...
使用`std::thread_local`关键字,可以在每个线程中存储一份局部变量的副本。这对于在多线程环境中实现线程特定的数据非常有用。 6. **线程优先级** 在Windows下,可以通过设置线程的优先级来控制其执行顺序。然而...
12. **线程局部变量**:ThreadLocal是线程局部变量的容器,每个线程都有自己的副本,互不影响。它常用于为线程提供独立的副本,避免了同步开销。 以上是对“多线程面试题”这一主题的简要概述,实际面试中可能会...
本文将深入探讨在Linux环境中进行多线程开发时的一些高效实践和注意事项。 首先,对于互斥锁的使用,Linux的Pthread库默认并不支持递归互斥锁。这意味着,如果一个线程已经持有了某个互斥锁,再次尝试加锁会引发...
由于静态局部变量的值在程序运行期间持续存在,如果程序中的多线程会同时访问同一个静态局部变量,就需要考虑线程安全的问题,以避免出现竞争条件导致的不一致现象。另外,在调试过程中,由于静态局部变量可能在程序...