Linux多线程实践(7) --多线程排序对比
屏障
int pthread_barrier_init(pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned count); int pthread_barrier_destroy(pthread_barrier_t *barrier); int pthread_barrier_wait(pthread_barrier_t *barrier);
屏障允许任意数量的线程等待, 直到所有的线程完成处理工作, 而线程不需要退出, 所有线程达到屏障之后可以接着工作.
init:在初始化屏障时, 可以使用第三个参数count指定, 在允许所有线程继续运行之前, 必须到达屏障的线程数目.
wait:可以使用pthread_barrier_wait函数来表明, 线程已经完成工作, 准备等所有其他线程赶上来;
调用wait的线程在屏障计数未满足条件时, 会进入休眠状态. 如果该线程是最后一个调用wait的线程, 就满足了屏障计数, 所有的线程都被唤醒.
对于一个任意线程, pthread_barrier_wait函数返回了PTHREAD_BARRIER_SERIAL_THREAD. 剩下的线程看到的返回值是0. 这使得一个线程可以作为主线程, 他可以工作在其他所有线程已完成的工作结果上.
单线程与多线程排序
单线程排序
bool compare(long a, long b) { return a < b; } #define NUMNUM 8000000L long int nums[NUMNUM]; //待排序数组(约32M) int main() { srandom(time(NULL)); for (unsigned long i = 0; i < NUMNUM; i++) nums[i] = random(); struct timeval start, end; //计时开始 gettimeofday(&start,NULL); sort(nums,nums+NUMNUM,compare); //单线程排序,快速排序 gettimeofday(&end,NULL); //计算用时 long long startusec = start.tv_sec * 1000000 + start.tv_usec; long long endusec = end.tv_sec * 1000000 + end.tv_usec; double elapsed = (double)(endusec - startusec) / 1000000.0; printf("sort took %.4f seconds\n", elapsed); //将排序后的结果写入文件, 以便查看是否已经排好序 FILE *fp = fopen("save.txt", "w+"); for (unsigned long i = 0; i < NUMNUM; i++) fprintf(fp, "%ld ", nums[i]); }
三次排序用时如下:
sort took 3.2435 seconds
sort took 3.2221 seconds
sort took 3.2134 seconds
(附-主机配置: 双核四线程(Intel(R) Core(TM) i3-2350M CPU @ 2.30GHz))
多线程排序(使用屏障同步)
#define NTHR 8 /* 线程数 */ #define NUMNUM 8000000L /* 待排序数 */ #define TNUM (NUMNUM/NTHR) /* 每个线程分配到的需要排序的数 */ long nums[NUMNUM]; long snums[NUMNUM]; pthread_barrier_t b; //屏障 bool compare(long a, long b) { return a < b; } //排序线程 //对nums数组的从idx~idx+TNUM部分进行快速排序 void *workThread(void *arg) { long idx = (long)arg; sort(&nums[idx],&nums[idx+TNUM],compare); pthread_barrier_wait(&b); pthread_exit(NULL); } //对已经排好序数组nums的NTHR部分进行合并 void merge() { long idx[NTHR]; //idx保存数组nums的NTHR部分的起始位置 for (long i = 0; i < NTHR; i++) idx[i] = i * TNUM; for (long sidx = 0; sidx < NUMNUM; sidx++) { long minidx; long num = LONG_MAX; //从NTHR部分的数组中查找出最小的一个, 将其index保存到idx[minidx]中 for (long i = 0; i < NTHR; i++) { //idx[i] < (i+1)*TNUM 确保是在一个部分之中, //不会产生两个部分相互比较的情况 if ((idx[i] < (i+1)*TNUM) && (nums[idx[i]] < num)) { num = nums[idx[i]]; minidx = i; } } snums[sidx] = nums[idx[minidx]]; idx[minidx]++; } } int main() { srandom(time(NULL)); for (unsigned long i = 0; i < NUMNUM; i++) nums[i] = random(); //创建NTHR个线程分别对数组相邻的NTHR部分进行排序 struct timeval start, end; pthread_t tid; gettimeofday(&start, NULL); pthread_barrier_init(&b, NULL, NTHR+1); for (unsigned long i = 0; i < NTHR; i++) pthread_create(&tid, NULL,workThread, (void *)(i * TNUM)); pthread_barrier_wait(&b); merge(); gettimeofday(&end, NULL); //计算用时 long long startusec = start.tv_sec * 1000000 + start.tv_usec; long long endusec = end.tv_sec * 1000000 + end.tv_usec; double elapsed = (double)(endusec - startusec) / 1000000.0; printf("sort took %.4f seconds\n", elapsed); //将排序后的结果写入文件, 以便查看是否已经排好序 FILE *fp = fopen("save.txt", "w+"); for (unsigned long i = 0; i < NUMNUM; i++) fprintf(fp, "%ld ", snums[i]); }
八线程排序:
sort took 1.5556 seconds
sort took 1.5676 seconds
sort took 1.5719 seconds
四线程排序:
sort took 1.4132 seconds
sort took 1.4315 seconds
sort took 1.4738 seconds
二线程排序:
sort took 2.0581 seconds
sort took 2.2358 seconds
sort took 1.7775 seconds
(附-主机配置: 双核四线程(Intel(R) Core(TM) i3-2350M CPU @ 2.30GHz))
总结: 可以看到尽管在分别进行排序之后还要有合并(merge)这一步,多线程排序(计算密集型任务)还是要优于单线程排序(在CPU为多核的情况下),而且在CPU为四线程下,使用四个线程对数组进行排序,所耗费时间是最少的!