昨天发了博文后,对并发编程开始有点兴趣,我就看了Paul E. McKenney大师的并行编程的那本书,那本书第四章重点将了多线程并发条件下的计数。有兴趣的同学可以看下,讲的比较详细。我就对大师基于每线程变量的统计计数器做了研究,实现了下。其实大师基本代码都已经敲出来了。我只是实现了一下,就当练练手,同时向大师致敬。
这个方法原理比较简单,就是每个线程维护自己的计数器,到了统计计数的时候,把每个线程的计数器的值加在一起,就是总计数器的值。当然加的时候要加锁保护。比较特殊的地方是定义了个
这个__thread声明的表示每个线程各自维护一个各自的int型变量 count,这就是江湖上传闻已久的TLS, Thread-Local Storage,有兴趣的同学可以学习Ulrich Drepper大牛的ELF handling of Thread-Local Storage。
这种方案使用场合是计数器自加很频繁,但是查看计数器的值不频繁的场合,因为查看总计数器需要加锁操作,而每个线程自加不需要加锁,每个线程各自维护各自的计数器。这种应用场景很普遍,前文中我需要设计的计数器就是这样。日常维护也许好长时间才看一样相关统计量。
下面上代码:
- #define _GNU_SOURCE
-
-
#include<stdio.h>
-
#include<pthread.h>
-
#include<stdlib.h>
-
#include<sys/poll.h>
-
#include<unistd.h>
-
#include<time.h>
-
#include<linux/types.h>
-
-
#define NR_THREADS 10
-
-
#define COUNT 5000
-
-
int __thread count = 0;
-
int *pcount[NR_THREADS] = {NULL};
-
int finalcount = 0;
-
-
-
pthread_spinlock_t g_spinlock;
-
-
void inc_count()
-
{
-
count++;
-
}
-
-
int read_count()
-
{
-
int t;
-
int sum ;
-
-
pthread_spin_lock(&g_spinlock);
-
sum = finalcount;
-
for(t = 0;t<NR_THREADS;t++)
- {
-
if(pcount[t] != NULL)
- sum += *pcount[t];
-
}
-
pthread_spin_unlock(&g_spinlock);
-
return sum;
-
}
-
-
-
typedef struct
-
{
-
int nr;
- pthread_t tid;
-
}Thread;
-
-
Thread Threads[NR_THREADS];
-
-
void count_register_thread(int idx)
-
{
-
pthread_spin_lock(&g_spinlock);
- pcount[idx] = &count;
-
pthread_spin_unlock(&g_spinlock);
-
}
-
-
void count_unregister_thread(int idx)
-
{
-
pthread_spin_lock(&g_spinlock);
- finalcount += count;
- pcount[idx] = NULL;
-
pthread_spin_unlock(&g_spinlock);
-
}
-
-
void *thr_function(void* arg)
-
{
-
int nr = *(int*)arg;
- fprintf(stderr,"the %d thread init successful\n",nr);
- struct timespec delay = {0};
- delay.tv_sec = 0;
- delay.tv_nsec = 1000000;
-
struct timeval tv_begin,tv_end;
-
__u64 interval = 0;
-
- count_register_thread(nr);
-
-
int i,j;
-
- gettimeofday(&tv_begin,NULL);
-
for(i = 0;i<COUNT;i++)
-
{
-
inc_count();
- nanosleep(&delay,NULL);
-
}
- gettimeofday(&tv_end,NULL);
- interval = (tv_end.tv_sec -tv_begin.tv_sec)*1000000 + (tv_end.tv_usec-tv_begin.tv_usec);
- fprintf(stderr,"the thread %d cost %llu us\n", nr,interval);
- count_unregister_thread(nr);
- fprintf(stderr,"the %d thread will exit\n",nr);
-
return NULL;
-
}
-
-
int main()
-
{
-
int i;
-
-
pthread_spin_init(&g_spinlock,0);
-
-
-
for(i= 0;i<NR_THREADS;i++)
-
{
-
Threads[i].nr = i;
- if(pthread_create(&Threads[i].tid,NULL, thr_function,&Threads[i].nr))
- {
- fprintf(stderr,"error happened when create thread %d\n",i);
- return 0;
- }
-
}
-
-
for(i= 0;i<10;i++)
-
{
-
- poll(NULL,0,500);
- fprintf(stderr,"MASTER PROC: the finalcount is %d now \n",read_count());
-
}
-
-
for(i = 0;i<NR_THREADS;i++)
-
{
-
if(pthread_join(Threads[i].tid,NULL))
-
{
- fprintf(stderr,"error happened when join the thread %d\n",i);
- return ;
-
}
-
}
-
-
fprintf(stderr,"MASTER PROC: at last finalcount is %d\n ",finalcount);
-
return 0;
-
}
运行结果如下:
- the 1 thread init successful
-
the 0 thread init successful
-
the 3 thread init successful
-
the 4 thread init successful
-
the 2 thread init successful
-
the 5 thread init successful
-
the 6 thread init successful
-
the 7 thread init successful
-
the 8 thread init successful
-
the 9 thread init successful
-
MASTER PROC: the finalcount is 4634 now
-
MASTER PROC: the finalcount is 9255 now
-
MASTER PROC: the finalcount is 13880 now
-
MASTER PROC: the finalcount is 18482 now
-
MASTER PROC: the finalcount is 23129 now
-
MASTER PROC: the finalcount is 27761 now
-
MASTER PROC: the finalcount is 32395 now
-
MASTER PROC: the finalcount is 36992 now
-
MASTER PROC: the finalcount is 41644 now
-
MASTER PROC: the finalcount is 46282 now
-
the thread 1 cost 5401657 us
-
the thread 4 cost 5401638 us
-
the 4 thread will exit
-
the 1 thread will exit
-
the thread 8 cost 5403640 us
-
the 8 thread will exit
-
the thread 7 cost 5404796 us
-
the 7 thread will exit
-
the thread 6 cost 5405765 us
-
the 6 thread will exit
-
the thread 2 cost 5405876 us
-
the 2 thread will exit
-
the thread 3 cost 5406989 us
-
the 3 thread will exit
-
the thread 0 cost 5410352 us
-
the 0 thread will exit
-
the thread 9 cost 5411182 us
-
the 9 thread will exit
-
the thread 5 cost 5412392 us
-
the 5 thread will exit
-
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。
阅读(478) | 评论(0) | 转发(0) |