Chinaunix首页 | 论坛 | 博客
  • 博客访问: 429183
  • 博文数量: 132
  • 博客积分: 2511
  • 博客等级: 大尉
  • 技术积分: 1385
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-11 15:10
文章分类

全部博文(132)

文章存档

2012年(18)

2011年(35)

2010年(60)

2009年(19)

分类: LINUX

2010-07-07 18:15:35

  本文原始链接: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,转载请注明
阅读(2330) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~