本文原始链接:http://blog.chinaunix.net/u/17287/showart.php?id=2267925,转载请注明
今天遇到了这样的问题:在移植现有的代码过程中,需要让现在的内存池的代码支持多线程。之前的引擎是只有一个线程在跑,所以内存池被声明成了一个全局的pool,需要内存时就从里面拿,目前的整个cache中所有的节点都是这样获取的,但在多线程环境下,这样显然是有问题的,有可能出现多个线程同时拿的情况。
最直观的解决方法就是加锁,用个mutex或者spinlock锁一下,每次取内存时,先获取锁,但这样带来的加锁的开销,有可能会抵销使用内存池带来的性能提升(当然,没做过测试,结果不好说,不过加锁的方式显然不好。)。
与牛人讨论后,得出以下方法:内存分配与使用分离,即每个线程都维护一个内存池,每个线程也都是从自己的内存池中拿内存,但写完后,将这块内存加入到cache中,因为cache是全局的,所以其它线程也可以访问到这些内存中的内容。
做了个实验验证上面的方法的可行性:把cache抽象成一个全局的链表,每个线程中分配一个结点,把这个结点加入链表中,看是否所有的线程都有访问链表中的内容。代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
typedef struct _node
{
pthread_t id;
struct _node *next;
}node;
typedef struct _head
{
pthread_mutex_t mutex;
node *next;
}head;
void* func(void *arg)
{
node *nd = malloc(sizeof(node));
node *p = NULL;
head *link = (head*)arg;
nd->id= pthread_self();
nd->next = NULL;
pthread_mutex_lock(&(link->mutex));
if(link->next == NULL)
{
link->next = nd;
}
else
{
p = link->next;
while(p->next != NULL)
{
p = p->next;
}
p->next = nd;
}
//print
p = link->next;
while(p != NULL)
{
printf("in thread,id:%u\n",p->id);
p = p->next;
}
printf("################\n");
pthread_mutex_unlock(&(link->mutex));
}
head *link = NULL;
int main()
{
node *p = NULL;
pthread_t pid;
int i;
link = malloc(sizeof(head));
//link->mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init(&(link->mutex),NULL);
link->next = NULL;
for(i = 0;i < 10;i ++)
{
pthread_create(&pid,NULL,func,(void*)link);
}
sleep(2);
p = link->next;
while(p != NULL)
{
printf("%u\n",p->id);
p = p->next;
}
return 0;
}
|
执行结果如下:
in thread,id:3086748560
################
in thread,id:3086748560
in thread,id:3076258704
################
in thread,id:3086748560
in thread,id:3076258704
in thread,id:3065768848
################
in thread,id:3086748560
in thread,id:3076258704
in thread,id:3065768848
in thread,id:3055278992
################
in thread,id:3086748560
in thread,id:3076258704
in thread,id:3065768848
in thread,id:3055278992
in thread,id:3044789136
################
in thread,id:3086748560
in thread,id:3076258704
in thread,id:3065768848
in thread,id:3055278992
in thread,id:3044789136
in thread,id:3034299280
################
in thread,id:3086748560
in thread,id:3076258704
in thread,id:3065768848
in thread,id:3055278992
in thread,id:3044789136
in thread,id:3034299280
in thread,id:3023809424
################
in thread,id:3086748560
in thread,id:3076258704
in thread,id:3065768848
in thread,id:3055278992
in thread,id:3044789136
in thread,id:3034299280
in thread,id:3023809424
in thread,id:3013319568
################
in thread,id:3086748560
in thread,id:3076258704
in thread,id:3065768848
in thread,id:3055278992
in thread,id:3044789136
in thread,id:3034299280
in thread,id:3023809424
in thread,id:3013319568
in thread,id:3002829712
################
in thread,id:3086748560
in thread,id:3076258704
in thread,id:3065768848
in thread,id:3055278992
in thread,id:3044789136
in thread,id:3034299280
in thread,id:3023809424
in thread,id:3013319568
in thread,id:3002829712
in thread,id:2992339856
################
3086748560
3076258704
3065768848
3055278992
3044789136
3034299280
3023809424
3013319568
3002829712
2992339856
表明所有的线程都能够访问到这些数据。
这个方法颠覆了我之前的认识:我一直以为在某个线程中的变量是不能被其它线程访问到的。但实际上这样的想法至少不是全对的,如果这个变量指向一块动态分配
的内存,那么这块内存是可以被其它线程访问的,因为所有 的线程共享地址空间,但如果只是一个普通变量,比如int
a,其它线程是无法访问的。可以简单的这样理解:同一个进程下的线程,它们的栈是独立的,但堆是共享的。。(未必是准确的说法)
于是决定每个线程中做一个pool,这个pool最好是在线程内部是全局的,哪儿都可以访问,这样就不需要把它在函数间传来传去,也就不用改现有函数接口。考虑了线程私有数据TSD,那就要保证这个线程私有数据在线程内部是全局可见的,开始做的实验是需要这个变量时,调用pthread_getspecific来得到它,但这样又增加了函数调用甚至系统调用,没准性能又下降。后来想到,试一下在main函数外面声明一个全局的pool,但在各线程内部将它用pthread_setspecific设为私有变量,看是什么情况。代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_key_t key;
typedef struct _node
{
pthread_t id;
struct _node *next;
}node;
typedef struct _head
{
pthread_mutex_t mutex;
node *next;
}head;
node *nd = NULL;
void print(head *link)
{
node *p = NULL;
//node *nd = NULL;
//nd = pthread_getspecific(key);
printf("nd--| id:%u\n",nd->id);
p = link->next;
while(p != NULL)
{
printf("in thread,id:%u\n",p->id);
p = p->next;
}
}
void* func(void *arg)
{
node *p = NULL;
//node *nd = NULL;
head *link = (head*)arg;
if ( (nd = pthread_getspecific(key)) == NULL) {
nd = malloc(sizeof(node));
pthread_setspecific(key,nd);
}
nd->id= pthread_self();
nd->next = NULL;
printf("id:%u,",pthread_self());
printf("nd address:%p,",&nd);
pthread_mutex_lock(&(link->mutex));
if(link->next == NULL)
{
link->next = nd;
}
else
{
p = link->next;
while(p->next != NULL)
{
p = p->next;
}
p->next = nd;
}
//print
print(link);
printf("################\n");
pthread_mutex_unlock(&(link->mutex));
}
head *link = NULL;
int main()
{
node *p = NULL;
pthread_t pid;
int i;
link = malloc(sizeof(head));
//link->mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init(&(link->mutex),NULL);
link->next = NULL;
pthread_key_create(&key,NULL);
for(i = 0;i < 10;i ++)
{
pthread_create(&pid,NULL,func,(void*)link);
}
sleep(2);
p = link->next;
while(p != NULL)
{
printf("%u\n",p->id);
p = p->next;
}
//we don't free nodes.
return 0;
}
|
执行结果如下:
id:3086838672,nd address:0x8049bb8,nd--| id:3086838672
in thread,id:3086838672
################
id:3076348816,nd address:0x8049bb8,nd--| id:3076348816
in thread,id:3086838672
in thread,id:3076348816
################
id:3065858960,nd address:0x8049bb8,nd--| id:3065858960
in thread,id:3086838672
in thread,id:3076348816
in thread,id:3065858960
################
id:3055369104,nd address:0x8049bb8,nd--| id:3055369104
in thread,id:3086838672
in thread,id:3076348816
in thread,id:3065858960
in thread,id:3055369104
################
id:3044879248,nd address:0x8049bb8,nd--| id:3044879248
in thread,id:3086838672
in thread,id:3076348816
in thread,id:3065858960
in thread,id:3055369104
in thread,id:3044879248
################
id:3034389392,nd address:0x8049bb8,nd--| id:3034389392
in thread,id:3086838672
in thread,id:3076348816
in thread,id:3065858960
in thread,id:3055369104
in thread,id:3044879248
in thread,id:3034389392
################
id:3023899536,nd address:0x8049bb8,nd--| id:3023899536
in thread,id:3086838672
in thread,id:3076348816
in thread,id:3065858960
in thread,id:3055369104
in thread,id:3044879248
in thread,id:3034389392
in thread,id:3023899536
################
id:3013409680,nd address:0x8049bb8,nd--| id:3013409680
in thread,id:3086838672
in thread,id:3076348816
in thread,id:3065858960
in thread,id:3055369104
in thread,id:3044879248
in thread,id:3034389392
in thread,id:3023899536
in thread,id:3013409680
################
id:3002919824,nd address:0x8049bb8,nd--| id:3002919824
in thread,id:3086838672
in thread,id:3076348816
in thread,id:3065858960
in thread,id:3055369104
in thread,id:3044879248
in thread,id:3034389392
in thread,id:3023899536
in thread,id:3013409680
in thread,id:3002919824
################
id:2992429968,nd address:0x8049bb8,nd--| id:2992429968
in thread,id:3086838672
in thread,id:3076348816
in thread,id:3065858960
in thread,id:3055369104
in thread,id:3044879248
in thread,id:3034389392
in thread,id:3023899536
in thread,id:3013409680
in thread,id:3002919824
in thread,id:2992429968
################
3086838672
3076348816
3065858960
3055369104
3044879248
3034389392
3023899536
3013409680
3002919824
2992429968
从上面的结果中可以看到,在各个线程中,全局的nd的地址是一样的,所以这样的方法是不行的。。难道只能每次需要这个全局变量时,都调用pthread_getspecific()来获得它?
同时今天又知道了另一种声明线程私有全局变量的方法,只需要用几个关键字声明一下即可,很简单,需要编译器支持,不过没记清,明天试一下。
本文原始链接:http://blog.chinaunix.net/u/17287/showart.php?id=2267925,转载请注明
阅读(2356) | 评论(0) | 转发(0) |