Chinaunix首页 | 论坛 | 博客
  • 博客访问: 492848
  • 博文数量: 133
  • 博客积分: 1235
  • 博客等级: 少尉
  • 技术积分: 1201
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-08 19:59
文章分类

全部博文(133)

文章存档

2023年(12)

2022年(3)

2018年(2)

2017年(4)

2016年(4)

2015年(42)

2014年(1)

2013年(12)

2012年(16)

2011年(36)

2010年(1)

分类:

2011-12-01 12:00:28

      昨天发了博文后,对并发编程开始有点兴趣,我就看了Paul E. McKenney大师的并行编程的那本书,那本书第四章重点将了多线程并发条件下的计数。有兴趣的同学可以看下,讲的比较详细。我就对大师基于每线程变量的统计计数器做了研究,实现了下。其实大师基本代码都已经敲出来了。我只是实现了一下,就当练练手,同时向大师致敬。

    这个方法原理比较简单,就是每个线程维护自己的计数器,到了统计计数的时候,把每个线程的计数器的值加在一起,就是总计数器的值。当然加的时候要加锁保护。比较特殊的地方是定义了个
  1. int __thread count = 0;
    这个__thread声明的表示每个线程各自维护一个各自的int型变量 count,这就是江湖上传闻已久的TLS, Thread-Local Storage,有兴趣的同学可以学习Ulrich Drepper大牛的ELF handling of Thread-Local Storage。

    这种方案使用场合是计数器自加很频繁,但是查看计数器的值不频繁的场合,因为查看总计数器需要加锁操作,而每个线程自加不需要加锁,每个线程各自维护各自的计数器。这种应用场景很普遍,前文中我需要设计的计数器就是这样。日常维护也许好长时间才看一样相关统计量。
    下面上代码:

  1. #define _GNU_SOURCE

  2. #include<stdio.h>
  3. #include<pthread.h>
  4. #include<stdlib.h>
  5. #include<sys/poll.h>
  6. #include<unistd.h>
  7. #include<time.h>
  8. #include<linux/types.h>

  9. #define NR_THREADS 10

  10. #define COUNT 5000

  11. int __thread count = 0;
  12. int *pcount[NR_THREADS] = {NULL};
  13. int finalcount = 0;


  14. pthread_spinlock_t g_spinlock;

  15. void inc_count()
  16. {
  17.     count++;
  18. }

  19. int read_count()
  20. {
  21.     int t;
  22.     int sum ;

  23.     pthread_spin_lock(&g_spinlock);
  24.     sum = finalcount;
  25.     for(t = 0;t<NR_THREADS;t++)
  26.     {    
  27.          if(pcount[t] != NULL)
  28.             sum += *pcount[t];
  29.     }
  30.     pthread_spin_unlock(&g_spinlock);
  31.     return sum;
  32. }


  33. typedef struct
  34. {
  35.    int nr;
  36.    pthread_t tid;
  37. }Thread;

  38. Thread Threads[NR_THREADS];

  39. void count_register_thread(int idx)
  40. {
  41.     pthread_spin_lock(&g_spinlock);
  42.     pcount[idx] = &count;
  43.     pthread_spin_unlock(&g_spinlock);
  44. }

  45. void count_unregister_thread(int idx)
  46. {
  47.     pthread_spin_lock(&g_spinlock);
  48.     finalcount += count;
  49.     pcount[idx] = NULL;
  50.     pthread_spin_unlock(&g_spinlock);
  51. }

  52. void *thr_function(void* arg)
  53. {
  54.    int nr = *(int*)arg;
  55.    fprintf(stderr,"the %d thread init successful\n",nr);
  56.    struct timespec delay = {0};
  57.    delay.tv_sec = 0;
  58.    delay.tv_nsec = 1000000;
  59.    struct timeval tv_begin,tv_end;
  60.    __u64 interval = 0;
  61.     
  62.    count_register_thread(nr);

  63.    int i,j;
  64.    
  65.    gettimeofday(&tv_begin,NULL);
  66.    for(i = 0;i<COUNT;i++)
  67.    {
  68.      inc_count();
  69.      nanosleep(&delay,NULL);
  70.    }
  71.    gettimeofday(&tv_end,NULL);

  72.    interval = (tv_end.tv_sec -tv_begin.tv_sec)*1000000               + (tv_end.tv_usec-tv_begin.tv_usec);
  73.    fprintf(stderr,"the thread %d cost %llu us\n",                        nr,interval);
  74.    count_unregister_thread(nr);
  75.    fprintf(stderr,"the %d thread will exit\n",nr);
  76.    return NULL;
  77. }

  78. int main()
  79. {
  80.     int i;

  81.     pthread_spin_init(&g_spinlock,0);


  82.     for(i= 0;i<NR_THREADS;i++)
  83.     {
  84.        Threads[i].nr = i;
  85.        if(pthread_create(&Threads[i].tid,NULL,                                  thr_function,&Threads[i].nr))
  86.        {
  87.             fprintf(stderr,"error happened when create                             thread %d\n",i);
  88.              return 0;
  89.        }
  90.     }

  91.     for(i= 0;i<10;i++)
  92.     {

  93.         poll(NULL,0,500);
  94.         fprintf(stderr,"MASTER PROC: the finalcount is                         %d now \n",read_count());
  95.     }

  96.     for(i = 0;i<NR_THREADS;i++)
  97.     {
  98.         if(pthread_join(Threads[i].tid,NULL))
  99.         {
  100.               fprintf(stderr,"error happened when join                               the thread %d\n",i);
  101.               return ;
  102.         }
  103.     }

  104.     fprintf(stderr,"MASTER PROC: at last finalcount is                     %d\n ",finalcount);
  105.     return 0;
  106. }
运行结果如下:
  1. the 1 thread init successful
  2. the 0 thread init successful
  3. the 3 thread init successful
  4. the 4 thread init successful
  5. the 2 thread init successful
  6. the 5 thread init successful
  7. the 6 thread init successful
  8. the 7 thread init successful
  9. the 8 thread init successful
  10. the 9 thread init successful
  11. MASTER PROC: the finalcount is 4634 now
  12. MASTER PROC: the finalcount is 9255 now
  13. MASTER PROC: the finalcount is 13880 now
  14. MASTER PROC: the finalcount is 18482 now
  15. MASTER PROC: the finalcount is 23129 now
  16. MASTER PROC: the finalcount is 27761 now
  17. MASTER PROC: the finalcount is 32395 now
  18. MASTER PROC: the finalcount is 36992 now
  19. MASTER PROC: the finalcount is 41644 now
  20. MASTER PROC: the finalcount is 46282 now
  21. the thread 1 cost 5401657 us
  22. the thread 4 cost 5401638 us
  23. the 4 thread will exit
  24. the 1 thread will exit
  25. the thread 8 cost 5403640 us
  26. the 8 thread will exit
  27. the thread 7 cost 5404796 us
  28. the 7 thread will exit
  29. the thread 6 cost 5405765 us
  30. the 6 thread will exit
  31. the thread 2 cost 5405876 us
  32. the 2 thread will exit
  33. the thread 3 cost 5406989 us
  34. the 3 thread will exit
  35. the thread 0 cost 5410352 us
  36. the 0 thread will exit
  37. the thread 9 cost 5411182 us
  38. the 9 thread will exit
  39. the thread 5 cost 5412392 us
  40. the 5 thread will exit
  41. MASTER PROC: at last finalcount is 50000
    通过结果可以看出以下几点:
1 总计数器是精确的,线程退出后,finalcount的值和预想值一致可以看出。
2 nanosleep 延迟并不精确。我理论运行时间是5秒,但是实际运行时间都是5.4秒。



参考文献:
1 Paul E. McKenney  Is Parallel Programming Hard, And, If So, What Can You Do About It?
2 Ulrich Drepper    ELF handling of Thread-Local Storage。
阅读(475) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~