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为四线程下,使用四个线程对数组进行排序,所耗费时间是最少的!

时间: 2024-10-23 04:08:06

Linux多线程实践(7) --多线程排序对比的相关文章

多线程实践—Python多线程编程

多线程实践 前面的一些文章和脚本都是只能做学习多线程的原理使用,实际上什么有用的事情也没有做.接下来进行多线程的实践,看一看在实际项目中是怎么使用多线程的. 图书排名示例 Bookrank.py: 该脚本通过单线程进行下载图书排名信息的调用 ? 1 from atexit import register 2 from re import compile 3 from threading import Thread 4 from time import sleep, ctime 5 import

Linux下C语言多线程,网络通信简单聊天程序

原文:Linux下C语言多线程,网络通信简单聊天程序 功能描述:程序应用多线程技术,可是实现1对N进行网络通信聊天.但至今没想出合适的退出机制,除了用Ctr+C.出于演示目的,这里采用UNIX域协议(文件系统套接字),程序分为客户端和服务端.应用select函数来实现异步的读写操作. 先说一下服务端:首先先创建套接字,然后绑定,接下进入一个无限循环,用accept函数,接受“连接”请求,然后调用创建线程函数,创造新的线程,进入下一个循环.这样每当有一个新的“连接”被接受都会创建一个新的线程,实现

Linux程序设计学习笔记----多线程编程基础概念与基本操作

转载请注明出处,http://blog.csdn.net/suool/article/details/38542543,谢谢. 基本概念 线程和进程的对比 用户空间资源对比 每个进程在创建的时候都申请了新的内存空间以存储代码段\数据段\BSS段\堆\栈空间,并且这些的空间的初始化值是父进程空间的,父子进程在创建后不能互访资源. 而每个新创建的线程则仅仅申请了自己的栈,空间,与同进程的其他线程共享该进程的其他数据空间包括代码段\数据段\BSS段\堆以及打开的库,mmap映射的文件与共享的空间,使得

深入 HTML5 Web Worker 应用实践:多线程编程

深入 HTML5 Web Worker 应用实践:多线程编程 HTML5 中工作线程(Web Worker)简介 至 2008 年 W3C 制定出第一个 HTML5 草案开始,HTML5 承载了越来越多崭新的特性和功能.它不但强化了 Web 系统或网页的表现性能,而且还增加了对本地数据库等 Web 应用功能的支持.其中,最重要的一个便是对多线程的支持.在 HTML5 中提出了工作线程(Web Worker)的概念,并且规范出 Web Worker 的三大主要特征:能够长时间运行(响应),理想的启

Linux程序设计学习笔记----多线程编程线程同步机制之互斥量(锁)与读写锁

互斥锁通信机制 基本原理 互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下: (1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待. (2)只有锁定该互斥锁的进程才能释放该互斥锁. 互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义. 互斥量

打造百度网盘备份利器:自动备份Linux VPS文件和多线程下载百度网盘资源

前一段时间国内的各大网盘百度云盘,金山快盘,360云盘,华为网盘为争夺用户上演空间容量博弈,网盘商们还固执地以为中国的网民都不懂网络技术,可以像某公司那样用一些数字的手段来忽悠用户,参与到网盘商的数字游戏中的公司都心知肚明. 在不需要增加任何网盘上传下载带宽和硬件设备投入的情况下,又可以用TB级别网盘这样的字眼来夺人眼球,在网络上大肆炒作一番,这比用钱买广告来推广网盘有效得多了.在这场网盘炒作中,率先“挑起事非”的两家百度网盘和360网盘获利最多. 尤其是百度网盘,应该算正式成长为国内网盘界中老

Linux高性能服务器编程——多线程编程(上)

多线程编程 Linux线程概述 线程模型 线程是程序中完成一个独立任务的完整执行序列,即一个可调度的实体.根据运行环境和调度者的身份,线程可分为内核线程和用户线程.内核线程,在有的系统上也称为LWP(Light Weigth Process,轻量级进程),运行在内核空间,由内核来调度:用户线程运行在用户空间,由线程库来调度.当进程的一个内核线程获得CPU的使用权时,它就加载并运行一个用户线程.可见,内核线程相当于用于线程运行的容器.一个进程可以拥有M个内核线程和N个用户线程,其中M≤N.并且在一

Linux高性能服务器编程——多线程编程(下)

多线程编程 条件变量 如果说互斥锁是用于同步线程对共享数据的访问的话,那么条件变量则是用于线程之间同步共享数据的值.条件变量提供了一种线程间的通信机制:当某个共享数据达到某个值得时候,唤醒等待这个共享数据的线程. 条件本身是由互斥量保护的.线程在改变条件状态前必须首先锁住互斥量,其他现成在获得互斥量之前不会察觉到这种变化,因为必须锁住互斥量以后才能计算条件. 条件变量的相关函数主要有如下5个: #include <pthread.h> int pthread_cond_destroy(pthr

Linux程序设计学习笔记----多线程编程之线程同步之条件变量

转载请注明出处:http://blog.csdn.net/suool/article/details/38582521. 基本概念与原理 互斥锁能够解决资源的互斥访问,但是在某些情况下,互斥并不能解决问题,比如两个线程需 要互斥的处理各自的操作,但是一个线程的操作仅仅存在一种条件成立的情况下执行,一旦错过不可再重现,由于线程间相互争夺cpu资源,因此在条件成立的时候,该线程不一定争夺到cpu而错过,导致永远得不到执行..... 因此需要某个机制来解决此问题,更重要的是,线程仅仅只有一种情况需要执