快速排序及多线程快排优化 & 选择 & 归并排序

一、写在前面

1.回调函数:用到函数指针,即指向函数的指针,这在代码框架结构设计中十分简洁高效,看代码


typedef enum tagSORT_FUNC
{
    SELECT_SORT= 0,
    MERGE_SORT,
    QUICK_SORT,
    SORT_FUNC_MAX
}SORT_FUNC_E;

typedef void (*SORT_CALLBACK_PF)(int *, int);

static SORT_CALLBACK_PF g_apfSortCallback[SORT_FUNC_MAX] = {NULL};

void SORT_RegCallBackFunc(SORT_FUNC_E type, SORT_CALLBACK_PF callBack)
{
    /* we thinks The default type > 0 */
    if (SORT_FUNC_MAX > type)
    {
        g_apfSortCallback[type] = callBack;
    }
}

/* use*/
int main ()
{
    int a [10];
    /callback /
    g_apfSortCallback[SELECT_SORT](a, 10);
    return ;
}

2.交换两个数

#define SWAP_INT_NUM(a, l, r)   \
{   \
    a[l] = a[l]^a[r];    \
    a[r] = a[l]^a[r];   \
    a[l] = a[l]^a[r];   \
}

3.线程同步机制:屏障

说起屏障这个东西,相信对于大多数朋友来说比较陌生,不过要是说起pthread_join这个函数,相信都比较熟悉。我们通常使用这个函数来等待其它线程结束,例如主线程创建一些线程,这些线程去完成一些工作,而主线程需要去等待这些线程结束。其实pthread_join就实现了一种屏障。我们可以对屏障这样理解,把屏障理解为为了协同线程之间的工作而使得某一具体线程进入等待状态的一种机制。下面我们来看看UNIX为我们提供的一个屏障——barrier。

注:与所有的线程函数一样,使用屏障barrier时需要包含头文件pthread.h,编译时需要添加参数 -lpthread

屏障barrier的相关结构:pthread_barrier_t  

初始化屏障结构函数:

int pthread_barrier_init(pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned int count);

函数执行成功返回 0,执行失败则返回一个错误号,我们可以通过该错误号获取相关的错误信息

函数第一个参数:一个指向pthread_barrier_t 类型的指针,我们必须要指出的是pthread_barrier_init函数不会给指针分配相关内存空间,因此我们传入的指针必须为一个pthread_barrier_t 变量

函数第二个参数:用于指定屏障的细节参数,我们这里可以暂且不去管它,如果我们传入NULL,那么系统将按照默认情况处理

函数第三个参数:设计屏障等待的最大线程数目,注意,主进程也可以作为线程计数。

销毁屏障函数:

int pthread_barrier_destory(pthread_barrier_t *barrier);

函数执行成功返回 0,执行失败则返回一个错误号,我们可以通过该错误号获取相关的错误信息

函数参数:指向pthread_barrier_t 变量的指针

我们需要注意的是,当一个屏障被使用完(即屏障已经等待了足够数目的线程后)就应该被销毁,故一个屏障我们不能复用,除非我们将它销毁并重新初始化

屏障等待函数:

int pthread_barrier_wait(pthread_barrier_t *barrier);

函数执行成功返回 0,执行失败则返回一个错误号,我们可以通过该错误号获取相关的错误信息

函数参数:指向pthread_barrier_t 变量的指针

一个线程需要等待其它多个线程结束时,调用该函数

这种“屏障”机制最大的特点就是最后一个执行wait的动作最为重要,就像赛跑时的起跑枪一样,它来之前所有人都必须等着。所以实际使用中,pthread_barrier_xxx常常用来让所有线程等待“起跑枪”响起后再一起行动。比如我们可以用pthread_create()生成100个线程,每个子线程在被create出的瞬间就会自顾自的立刻进入回调函数运行。但我们可能不希望它们这样做,因为这时主进程还没准备好,和它们一起配合的其它线程还没准备好,我们希望它们在回调函数中申请完线程空间、初始化后停下来,一起等待主进程释放一个“开始”信号,然后所有线程再开始执行业务逻辑代码。

      为了解决上述场景问题,我们可以在init时指定n+1个等待,其中n是线程数。而在每个线程执行函数的首部调用wait()。这样100个pthread_create()结束后所有线程都停下来等待最后一个wait()函数被调用。这个wait()由主进程在它觉得合适的时候调用就好。

  在这里我们需要注意的是使用barrier这个屏障我们无法获取线程的结束状态,若想要获取相关线程结束状态我们仍然需要调用pthread_join函数。当然我们一般也不会把pthread_barrier_wait 放在某个线程结束时候,这显然是很无聊的,这个函数调用往往出现在线程之间的某个位置,接下来线程等待其它协同线程到达屏障后再处理一些其它事务。

二、三种常用排序:选择,快速,归并排序;

1.选择排序:


/*********************************************************
*Function Name   :SelectSort
*Descr           :sort number by selection format
*Auth            :lily
*Date            :2018-08-05
*Modify Recoder  :
*********************************************************/
static void  SelectSort(int a[], int n)
{
    int i, j;
    int pos;

    for (i = 0; i < n-1; i++)
    {
        pos = i;
        for (j = i+1; j < n; j++)
        {
            if(a[j] < a[pos])
            {
                pos = j;
            }
        }
        if (pos != i)
        {
            SWAP_INT_NUM(a, pos, i);
        }
    }

    return ;
}

2.快速排序:

/*********************************************************
*Function Name   :quick_sort
*Descr           :sort number by device sort format
*Auth            :lily
*Date            :2018-08-05
*Modify Recoder  :
*********************************************************/
static void quick_sort(int a[], int l, int r)
{
    int i,j,key;
    
    if (l < r)
    {
        i = l;
        j = r;
        key = a[i];/* could yes to use random numbers */


        while (i < j)/* until i == j */
        {
            while (i < j && a[j] > key)
            {
                j--;/* find the first num less than key num form right to left */
            }
            if (i < j)
            {
                a[i++] = a[j];
            }

            while (i < j && a[i] < key)
            {
                i++;/* find the first num more than key num form left to right */
            }
            if (i < j)
            {
                a[j--]= a[i];
            }
        }
        /* fill the key number to current empty subscript */
        a[i] = key;

        quick_sort(a, l, i-1);/* sort left part*/
        quick_sort(a, i+1, r);/* sort right part */
    }
    return ;
}

static void QuickSort(int  a[], int n)
{
    quick_sort(a, 0, n-1);
    return ;
}

3.归并排序:注意,这里为了提高效率,在外面申请足够资源,避免递归中申请资源影响速度。

/*********************************************************
*Function Name   :merge_arrary
*Descr           :merge two arry by order
*Auth            :lily
*Date            :2018-08-05
*Modify Recoder  :
*********************************************************/
static void merge_array(int a[], int first, int mid, int last, int c[])
{
    int i = first, j = mid+1;
    int m = mid, n = last;
    int k = 0;

    while (i <= m && j <= n)
    {
        if (a[i] < a[j])
        {
            c[k++] = a[i++];
        }
        else
        {
            c[k++] = a[j++];
        }
    }

    while (i <= m)
    {
        c[k++] = a[i++];
    }
    while (j <= n)
    {
        c[k++] = a[j++];
    }

    for(i = 0; i < k; i++)
    {
        a[first+ i] = c[i];
    }

    return ;
}

/*********************************************************
*Function Name   :merge_sort
*Descr           :sort number by device merge sort format
*Auth            :lily
*Date            :2018-08-05
*Modify Recoder  :
*********************************************************/
static void merge_sort(int a[], int first, int last, int c[])
{
    if (first < last)
    {
        int mid = (first + last)/2;
        merge_sort(a , first, mid,   c);/* left part */
        merge_sort(a , mid+1,  last, c);/* right part */
        merge_array(a , first, mid,  last, c);/* merge array */
    }
}

/*********************************************************
*Function Name   :MergeSort
*Descr           :sort number by device merge sort format
*Auth            :lily
*Date            :2018-08-05
*Modify Recoder  :
*********************************************************/
void MergeSort(int a[], int n)
{
    /* alloc the total len array pointer one time */
    int *p = malloc(sizeof(int) * n);
    if (NULL == p)
    {
        printf("malloc resource failed.\n");
    }

    merge_sort(a, 0, n-1, p);
    free(p);
}

三、多线程优化排序的一个范例,可通用

常规排序对于数千或数万级别的数据进行排序,对于现代处理器,特别是多核处理器可能耗时影响并不大,但是如果数据量上升到百万,甚至千万级别(当然这种工作简直就是自己找事做),常规单进程的排序可能就心有余而力不足了,这时候 ,多线程排序就成了其中一个优化的选择。对于多线程排序,我参考了UNIX环境高级编程中的实例,对排序进行优化,其中用到一个不常用的线程控制或者说是同步机制——屏障。关于屏障的介绍,在前面已经简单说过,实际上 我们只需要会使用必须的三个函数就可以。

假设有800万个数据等待排序,这里,800万个整型数据,我们申请资源或者用全局变量,仿照归并排序的思想。例如,我们可以创建8个线程,每个线程负责100万个数据的排序,最后由主进程来完成这8个100万数据的合并操作。这其中就用到最关键的一个函数:

int pthread_barrier_wait(pthread_barrier_t *barrier);

这个函数可以使得调用它的线程进行计数,并进入等待,只有最后一个线程或进程完成计数功能并满足条件后,所有线程才会被唤醒继续执行其他任务。

1.这里我们定义两个全局变量,并对全局变量进行初始化,给随机值:

#define THREAD_NUM 8
#define TOTAL_NUM 8000000
#define PER_THREAD_PROC (TOTAL_NUM / THREAD_NUM)

//pthread_barrier_t barrier; 这个就是多线程同步机制——屏障
int *g_pOriNum = NULL;
int *g_pSortNum = NULL;/* 排序后的数组 */

初始化:

void SORT_Init()
{
    int *p1 = NULL;
    int *p2 = NULL;

    p1 = (int *)malloc(sizeof(int) * TOTAL_NUM);
    p2 = (int *)malloc(sizeof(int) * TOTAL_NUM);
    if (NULL != p1 && NULL != p2)
    {
        memset(p1, 0 , (size_t)sizeof(int) * TOTAL_NUM);
        memset(p2, 0 , (size_t)sizeof(int) * TOTAL_NUM);

        g_pOriNum = p1;
        g_pSortNum = p2;

        return ;
    }

    if (NULL != p1)
    {
        free(p1);
    }

    if (NULL != p2)
    {
        free(p2);
    }

    
    return;
}

3.定义线程执行函数:

void thread_routine(void *pData)
{
    long index = *(unsigned long *)(pData);

    printf("thread %d beagin begain sort,, OriNum[%d]= %d.\n", index/PER_THREAD_PROC, index, g_pOriNum[index]);
    if (index <= TOTAL_NUM - PER_THREAD_PROC)
    {
        /* 这里换成QuickSort函数也是一样的,注意,入参是数组下标及每个线程负责排序的数据数 */
        g_apfSortCallback[QUICK_SORT](&g_pOriNum[index], PER_THREAD_PROC);
    }
    pthread_barrier_wait(&barrier);

    return ;
}

4.创建线程,使用屏障,排序

void pthread_sort()
{
    unsigned long i = 0;
    struct timeval start,end;
    long long startusec, endusec;
    double elaps;
    pthread_t tid[THREAD_NUM];
    int err = 0;
    long thread_arg[THREAD_NUM];
    long Index = 0;

    /* create 8 pthreads to sort numbers */
    gettimeofday(&start, NULL);

    printf("pthread barrier init...\n");
    pthread_barrier_init(&barrier, NULL, THREAD_NUM + 1);

    for(i = 0; i < THREAD_NUM; i++)
    {
        printf("create thread [%d].\n", i);
        thread_arg[i] = (long)(i * PER_THREAD_PROC); /*0,1000000, ... 7000000 */
        /* 此处的thread_arg 最好就用数组,不要用单个变量重复赋值传入,这里是地址   */
        err = pthread_create(&tid[i], NULL, thread_routine, (void *)&thread_arg[i]);
        if (0 != err)
        {
            perror("create thread failed");
        }
    }

    /* 主进程完成最后的计数并合并结果 */
    pthread_barrier_wait(&barrier);
    printf("merge...\n");
    Resturct_SortedArr();/* g_pSortNum[i] = g_pOriNum[j] */

    gettimeofday(&end, NULL);
    startusec =start.tv_sec * 1000000 + start.tv_usec;
    endusec =end.tv_sec * 1000000 + end.tv_usec;
    elaps = (double)(endusec - startusec)/1000000.0;

    //print_resut(g_pSortNum, TOTAL_NUM);

    for(i = 0; i < THREAD_NUM; i++)
    {
        printf(" g_pSortNum[%ld] = %ld\n", (i * PER_THREAD_PROC), g_pSortNum[(i * PER_THREAD_PROC)]);
    }
    
    printf("sort %d numbers took %.4f seconds\n",TOTAL_NUM, elaps);

    /* 回收线程资源 */
    pthread_barrier_destroy(&barrier);
    
    for(i = 0; i < THREAD_NUM; i++)
    {
        pthread_join(tid[i], NULL);
        printf("pthread_join [%d]\n", (unsigned int)i);
    }

    return ;
}

1.主函数,给定随机值

int main(int argc, char *argv[])
{
    int a[MAX_ARR_NUM] = {8,5,3,7,9,23,4,6,2,1};
    long i = 0;
    long min = 0;
    long max = 0;


    SORT_Init();

    printf("get random numbers...");
    if (NULL != g_pOriNum)
    {
        srand((unsigned)(time(NULL)));/* product   diffrent numbers every time*/
        g_pOriNum[0] =  rand();
        min = max = g_pOriNum[0];
        for (i = 1; i < TOTAL_NUM; i++)
        {
           g_pOriNum[i] =  rand();
           
           if (min > g_pOriNum[i])
           {
                min = g_pOriNum[i];/* 记录最小值 */
           }
           if (max < g_pOriNum[i])
           {
                max = g_pOriNum[i];/* 记录最大值 */
           }
        }
    }

    for(i = 0; i < THREAD_NUM; i++)
    {
        printf(" g_pOriNum[%ld] = %ld\n", (i * PER_THREAD_PROC), g_pOriNum[(i * PER_THREAD_PROC)]);
    }
    
    
    printf("sort begin, min = %d, max = %d...\n", min, max);
        
    pthread_sort();
    
    //printf("before sort\n");
    //print_resut(a, MAX_ARR_NUM);
    
    //g_apfSortCallback[SELECT_SORT](a, MAX_ARR_NUM);
    
    //printf("after sort\n");
    //print_resut(a, MAX_ARR_NUM);
    
    /* free  resource */
    
    printf("sort end...,g_pSortNum[begin]= %d, g_pSortNum[end] = %d.\n", 
                                g_pSortNum[0], g_pSortNum[TOTAL_NUM -1]);

    /* last step */
    SORT_Fini();
    //TEST_Sort();

    return 0;
}

四、附上完整源代码及测试结果

#ifdef __cplusplus             //告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的
extern "C"{
#endif


#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
#include <string.h>


#define TRUE 1

#define FALSE 0




#define LONG_MAX 2147483647L

#define MAX_ARR_NUM 10

#define THREAD_NUM 8
#define TOTAL_NUM 8000000
#define PER_THREAD_PROC (TOTAL_NUM / THREAD_NUM)

static int g_bUseThread = TRUE;

pthread_barrier_t barrier;
int *g_pOriNum = NULL;
int *g_pSortNum = NULL;/* 排序后的数组 */


typedef enum tagSORT_FUNC
{
    SELECT_SORT= 0,
    MERGE_SORT,
    QUICK_SORT,
    SORT_FUNC_MAX
}SORT_FUNC_E;

typedef void (*SORT_CALLBACK_PF)(long *, int);

static SORT_CALLBACK_PF g_apfSortCallback[SORT_FUNC_MAX] = {NULL};

void SORT_RegCallBackFunc(SORT_FUNC_E type, SORT_CALLBACK_PF callBack)
{
    /* we thinks The default type > 0 */
    if (SORT_FUNC_MAX > type)
    {
        g_apfSortCallback[type] = callBack;
    }
}

void SORT_DeRegCallBackFunc()
{
    int i = 0;

    for(i = 0; i < SORT_FUNC_MAX; i ++)
    {
        g_apfSortCallback[i] = NULL;
    }
}


int IsUsethread()
{
    return g_bUseThread;
}

#define SWAP_INT_NUM(a, l, r)   \
{   \
    a[l] = a[l]^a[r];    \
    a[r] = a[l]^a[r];   \
    a[l] = a[l]^a[r];   \
}

/*********************************************************
*Function Name   :SelectSort
*Descr           :sort number by selection format
*Auth            :lily
*Date            :2018-08-05
*Modify Recoder  :
*********************************************************/
static void  SelectSort(int a[], int n)
{
    int i, j;
    int pos;

    for (i = 0; i < n-1; i++)
    {
        pos = i;
        for (j = i+1; j < n; j++)
        {
            if(a[j] < a[pos])
            {
                pos = j;
            }
        }
        if (pos != i)
        {
            SWAP_INT_NUM(a, pos, i);
        }
    }

    return ;
}

/*********************************************************
*Function Name   :quick_sort
*Descr           :sort number by device sort format
*Auth            :lily
*Date            :2018-08-05
*Modify Recoder  :
*********************************************************/
static void quick_sort(int a[], int l, int r)
{
    int i,j,key;
    
    if (l < r)
    {
        i = l;
        j = r;
        key = a[i];/* could yes to use random numbers */


        while (i < j)/* until i == j */
        {
            while (i < j && a[j] > key)
            {
                j--;/* find the first num less than key num form right to left */
            }
            if (i < j)
            {
                a[i++] = a[j];
            }

            while (i < j && a[i] < key)
            {
                i++;/* find the first num more than key num form left to right */
            }
            if (i < j)
            {
                a[j--]= a[i];
            }
        }
        /* fill the key number to current empty subscript */
        a[i] = key;

        quick_sort(a, l, i-1);/* sort left part*/
        quick_sort(a, i+1, r);/* sort right part */
    }
    return ;
}

static void QuickSort(int  a[], int n)
{
    printf(" quick sort ...\n ");
    quick_sort(a, 0, n-1);
    return ;
}

/*********************************************************
*Function Name   :merge_arrary
*Descr           :merge two arry by order
*Auth            :lily
*Date            :2018-08-05
*Modify Recoder  :
*********************************************************/
static void merge_array(int a[], int first, int mid, int last, int c[])
{
    int i = first, j = mid+1;
    int m = mid, n = last;
    int k = 0;

    while (i <= m && j <= n)
    {
        if (a[i] < a[j])
        {
            c[k++] = a[i++];
        }
        else
        {
            c[k++] = a[j++];
        }
    }

    while (i <= m)
    {
        c[k++] = a[i++];
    }
    while (j <= n)
    {
        c[k++] = a[j++];
    }

    for(i = 0; i < k; i++)
    {
        a[first+ i] = c[i];
    }

    return ;
}

/*********************************************************
*Function Name   :merge_sort
*Descr           :sort number by device merge sort format
*Auth            :lily
*Date            :2018-08-05
*Modify Recoder  :
*********************************************************/
static void merge_sort(int a[], int first, int last, int c[])
{
    if (first < last)
    {
        int mid = (first + last)/2;
        merge_sort(a , first, mid,   c);/* left part */
        merge_sort(a , mid+1,  last, c);/* right part */
        merge_array(a , first, mid,  last, c);/* merge array */
    }
}

/*********************************************************
*Function Name   :MergeSort
*Descr           :sort number by device merge sort format
*Auth            :lily
*Date            :2018-08-05
*Modify Recoder  :
*********************************************************/
void MergeSort(int a[], int n)
{
    /* alloc the total len array pointer one time */
    int *p = malloc(sizeof(int) * n);
    if (NULL == p)
    {
        printf("malloc resource failed.\n");
    }

    merge_sort(a, 0, n-1, p);
    free(p);
}

long long GetCurrentTime()
{
    struct timeval time;

    gettimeofday(&time,NULL);
    
    return (time.tv_sec * 1000000 + time.tv_usec);
}

double CalcTime_inusec(long long startusec, long long endusec)
{
    return (double)(endusec - startusec)/1000000.0;;
}


void print_resut(int a[], int n)
{
    int i = 0;
    int count = 0;
    for (; i < n; i++)
    {
        printf("%d\t", a[i]);
        count ++;
        if (count % 10 == 0)
        {
            printf("\n");
        }
    }
    printf("\n");
}


void SORT_Init()
{
    int *p1 = NULL;
    int *p2 = NULL;
    
    SORT_RegCallBackFunc(SELECT_SORT, SelectSort);
    SORT_RegCallBackFunc(MERGE_SORT,  MergeSort);
    SORT_RegCallBackFunc(QUICK_SORT,  QuickSort);

    p1 = (int *)malloc(sizeof(int) * TOTAL_NUM);
    p2 = (int *)malloc(sizeof(int) * TOTAL_NUM);
    if (NULL != p1 && NULL != p2)
    {
        memset(p1, 0 , (size_t)sizeof(int) * TOTAL_NUM);
        memset(p2, 0 , (size_t)sizeof(int) * TOTAL_NUM);

        g_pOriNum = p1;
        g_pSortNum = p2;

        return ;
    }

    if (NULL != p1)
    {
        free(p1);
    }

    if (NULL != p2)
    {
        free(p2);
    }

    
    return;
}


 void Resturct_SortedArr()
 {
    long index[THREAD_NUM];
    long i, minIndex, j ,key;

    for(i = 0; i < THREAD_NUM; i++)
    {               /* 8 */
        index[i] = i * PER_THREAD_PROC; /* 8 parts of one array */
    }                 /* 1000000 */

    for(j = 0; j < TOTAL_NUM; j++)
    {              /* 8000000 */          
        key = LONG_MAX;
        for(i = 0; i < THREAD_NUM; i++)
        {               /* 8 */
            if ((index[i] < (i+1) * PER_THREAD_PROC)/* 1000000 */
                && (g_pOriNum[index[i]] < key))
            {
                key = g_pOriNum[index[i]];
                minIndex = i;
            }
        }

        g_pSortNum[j] = g_pOriNum[index[minIndex]];
        index[minIndex]++;
    }
 }

void SORT_Fini()
{
    int *p1 = NULL;
    int *p2 = NULL;
    
    SORT_DeRegCallBackFunc();

    if (NULL != g_pOriNum)
    {
        free(g_pOriNum);
    }

    if (NULL != g_pSortNum)
    {
        free(g_pSortNum);
    }

    
    return;
}


void thread_routine(void *pData)
{
    long index = *(unsigned long *)(pData);

    printf("thread %d beagin begain sort,, OriNum[%d]= %d.\n", index/PER_THREAD_PROC, index, g_pOriNum[index]);
    if (index <= TOTAL_NUM - PER_THREAD_PROC)
    {
        g_apfSortCallback[QUICK_SORT](&g_pOriNum[index], PER_THREAD_PROC);
    }
    pthread_barrier_wait(&barrier);

    return ;
}



void pthread_sort()
{
    unsigned long i = 0;
    long long startusec, endusec;
    double elaps;
    pthread_t tid[THREAD_NUM];
    int err = 0;
    long thread_arg[THREAD_NUM];
    long Index = 0;

    /* create 8 pthreads to sort numbers */
    
    startusec = GetCurrentTime();
    
    printf("pthread barrier init...\n");
    pthread_barrier_init(&barrier, NULL, THREAD_NUM + 1);

    for(i = 0; i < THREAD_NUM; i++)
    {
        printf("create thread [%d].\n", i);
        thread_arg[i] = (long)(i * PER_THREAD_PROC); /*0,1000000, ... 7000000 */
        /* 此处的thread_arg 最好就用数组,不要用单个变量重复赋值传入,这里是地址   */
        err = pthread_create(&tid[i], NULL, thread_routine, (void *)&thread_arg[i]);
        if (0 != err)
        {
            perror("create thread failed");
        }
    }

    /* 主进程完成最后的计数并合并结果 */
    pthread_barrier_wait(&barrier);
    printf("merge...\n");
    Resturct_SortedArr();/* g_pSortNum[i] = g_pOriNum[j] */


    endusec = GetCurrentTime();
    elaps = CalcTime_inusec(startusec, endusec);

    //print_resut(g_pSortNum, TOTAL_NUM);

    for(i = 0; i < THREAD_NUM; i++)
    {
        printf(" g_pSortNum[%ld] = %ld\n", (i * PER_THREAD_PROC), g_pSortNum[(i * PER_THREAD_PROC)]);
    }
    
    printf("sort %d numbers took %.4f seconds\n",TOTAL_NUM, elaps);

    /* 回收线程资源 */
    pthread_barrier_destroy(&barrier);
    
    for(i = 0; i < THREAD_NUM; i++)
    {
        pthread_join(tid[i], NULL);
        printf("pthread_join [%d]\n", (unsigned int)i);
    }

    return ;
}

void TEST_Sort(SORT_FUNC_E type, long arr[], long n)
{
    long long startusec, endusec;
    double elaps;
    const char *apstr[SORT_FUNC_MAX] = 
                        {"Select sort",
                         "Merge sort",
                         "Quick sort",
                         "Unknown type"
                        };
                        
    printf("sort type :%s.", apstr[type]);
    startusec = GetCurrentTime();
     
    /* sort function callback*/
    if (SORT_FUNC_MAX > type)
    {
        g_apfSortCallback[type](arr, n);
    }

    endusec = GetCurrentTime();
    elaps = CalcTime_inusec(startusec, endusec);

    printf("sort %d numbers took %.4f seconds\n",n, elaps);
    
    return ;
}


int main(int argc, char *argv[])
{
    int a[MAX_ARR_NUM] = {8,5,3,7,9,23,4,6,2,1};
    long i = 0;
    long min = 0;
    long max = 0;
    int bUseThread = FALSE;


    SORT_Init();

    printf("get random numbers...");
    if (NULL != g_pOriNum)
    {
        srand((unsigned)(time(NULL)));/* product   diffrent numbers every time*/
        g_pOriNum[0] =  rand();
        min = max = g_pOriNum[0];
        for (i = 1; i < TOTAL_NUM; i++)
        {
           g_pOriNum[i] =  rand();
           
           if (min > g_pOriNum[i])
           {
                min = g_pOriNum[i];/* 记录最小值 */
           }
           if (max < g_pOriNum[i])
           {
                max = g_pOriNum[i];/* 记录最大值 */
           }
        }
    }

    for(i = 0; i < THREAD_NUM; i++)
    {
        printf(" g_pOriNum[%ld] = %ld\n", (i * PER_THREAD_PROC), g_pOriNum[(i * PER_THREAD_PROC)]);
    }
    
    
    printf("sort begin, min = %d, max = %d...\n", min, max);

    bUseThread = IsUsethread();
    if (TRUE == bUseThread)
    {
        /* 使用线程排序 */
        pthread_sort();
    }
    else
    {
        /* 使用普通快速排序 */
        TEST_Sort(QUICK_SORT, g_pOriNum, TOTAL_NUM);
    }

    
    if (TRUE == bUseThread)
    {
        printf("sort end...,g_pSortNum[begin]= %d, g_pSortNum[end] = %d.\n", 
                                g_pSortNum[0], g_pSortNum[TOTAL_NUM -1]);
    }
    else
    {
        printf("sort end...,g_pSortNum[begin]= %d, g_pSortNum[end] = %d.\n", 
                                g_pOriNum[0], g_pOriNum[TOTAL_NUM -1]);
    }

    /* last step */
    SORT_Fini();
    //TEST_Sort();

    return 0;
}

#ifdef __cplusplus
}
#endif

以上源代码用到回调函数机制 ,是否用线程排序直接修改全局变量g_bUseThread, TRUE为使用,FALSE为不使用。用800万个随机数测试,不使用线程的快速排序与使用线程的优化排序,结果如下:

使用线程排序800万个数,用到g_SortNum[]与g_OriNum[],OriNum[]是由随机数生成的无序数组,g_SortNum[]是由OriNum[]经过线程排序重组后的有序数组,使用线程快速排序在4核8线程CPU上速度比非线程普通快速排序快了一倍多,如果是单核CPU,或者加大数据量可能就体现出多线程的优势。

1.使用多线程的结果:

li@li:~/C_build/epoll_server$ ./thread_sort 
get random numbers... g_pOriNum[0] = 78801052
 g_pOriNum[1000000] = 886283732
 g_pOriNum[2000000] = 837031209
 g_pOriNum[3000000] = 149767515
 g_pOriNum[4000000] = 1464012217
 g_pOriNum[5000000] = 204910220
 g_pOriNum[6000000] = 1614270886
 g_pOriNum[7000000] = 1292514100
sort begin, min = 128, max = 2147482963...
pthread barrier init...
create thread [0].
create thread [1].
create thread [2].
create thread [3].
thread 0 beagin begain sort,, OriNum[0]= 78801052.
thread 1 beagin begain sort,, OriNum[1000000]= 886283732.
create thread [4].
 quick sort ...
 create thread [5].
create thread [6].
create thread [7].
 quick sort ...
 thread 4 beagin begain sort,, OriNum[4000000]= 1464012217.
 quick sort ...
 thread 5 beagin begain sort,, OriNum[5000000]= 204910220.
 quick sort ...
 thread 6 beagin begain sort,, OriNum[6000000]= 1614270886.
 quick sort ...
 thread 3 beagin begain sort,, OriNum[3000000]= 149767515.
 quick sort ...
 thread 2 beagin begain sort,, OriNum[2000000]= 837031209.
 quick sort ...
 thread 7 beagin begain sort,, OriNum[7000000]= 1292514100.
 quick sort ...
 merge...
 g_pSortNum[0] = 128
 g_pSortNum[1000000] = 268656908
 g_pSortNum[2000000] = 536739088
 g_pSortNum[3000000] = 805496162
 g_pSortNum[4000000] = 1074063497
 g_pSortNum[5000000] = 1341971849
 g_pSortNum[6000000] = 1610859668
 g_pSortNum[7000000] = 1879386104
sort 8000000 numbers took 0.6152 seconds
pthread_join [0]
pthread_join [1]
pthread_join [2]
pthread_join [3]
pthread_join [4]
pthread_join [5]
pthread_join [6]
pthread_join [7]
sort end...,g_pSortNum[begin]= 128, g_pSortNum[end] = 2147482963.
2.使用普通快速排序算法

li@li:~/C_build/epoll_server$ ./nothread_sort 
get random numbers... g_pOriNum[0] = 488501281
 g_pOriNum[1000000] = 904295600
 g_pOriNum[2000000] = 1546116108
 g_pOriNum[3000000] = 2132677164
 g_pOriNum[4000000] = 1430509994
 g_pOriNum[5000000] = 1674145008
 g_pOriNum[6000000] = 946527686
 g_pOriNum[7000000] = 1087497785
sort begin, min = 109, max = 2147482830...
sort type :Quick sort. quick sort ...
 sort 8000000 numbers took 1.3166 seconds
sort end...,g_pSortNum[begin]= 109, g_pSortNum[end] = 2147482830.
 

猜你喜欢

转载自blog.csdn.net/qq_33195791/article/details/81812385
今日推荐