使用Pthread实现并行快速排序程序

记录一次使用pthread编程实现并行快速排序程序过程。

一、快排

快速排序算法时间复杂度为O(nlogn),流程如下:
① 从数组序列中选出一个元素,称为中间值(pivot)
② 把数组序列分成两个子序列,比中间值小的元素放在一个子序列,比中间值大的元素放
在另一个子序列。该操作称为分割(partition)
③ 递归地对两个子序列分别重复上述操作,直到子序列中元素个数为1

二、程序设计

1.串行程序

观察快排程序流程,可以发现创建子序列存放数组两个部分等价于将需要交换位置的元素在原数组中进行交换,这样可以节省空间,其程序可写为:

#include <stdio.h>
#include <math.h>
#define N 20

void quik_sort(double *num,int left,int right){
    if (right<=left){
        return;
    }
    int start=left,end=right;
    double mid=0;
    while (left<right){
        while (left<right and num[right]>=num[start]){
            right-=1;
        }
        while (left<right and num[left]<=num[start]){
            left+=1;
        }
        mid=num[left];
        num[left]=num[right];
        num[right]=mid;
    }
    mid=num[start];
    num[start]=num[left];
    num[left]=mid;
    quik_sort(num,start,left-1);
    quik_sort(num,left+1,end);
}

int main(){

    double num[N];
    srand(1);//保持一致性
    
    for(int i=0;i<N;i++){
        num[i]=(double)rand()/RAND_MAX;
    }
    for(int i=0;i<N;i++){
        printf("%lf\n",num[i]);
    }
    quik_sort(num,0,N-1);
    
    for(int i=0;i<N;i++){
        printf("later:%lf\n",num[i]);
    }
    return 0;
}

这种情况下,我们可以发现该算法其实分为两步,第一步为找到中间值所在位置索引,第二步为递归地为两边的数组进行快排,由于第二步所需子数组由第一步确定,所以这两步不能并行,那我们的并行化思路有三种:

若我们对第一步进行并行,将寻找需要交换的两个元素分给两个线程执行,在需要大量交换操作时,这种方法用于管理线程的开销可能会导致其不如串行程序;

若我们对第二步进行并行,将两个递归操作交给两个线程来完成,那么我们将需要创建规模为O(logn)个线程用于递归操作,导致我们需要使用mutex对线程数量进行控制。

若我们对数据进行并行,将数组分为线程数量个数的子数组,每个线程负责一块的快速排序,最后再将这几块有序数组按归并排序的方法在O(nlogn)的时间内组合起来,但这就需要额外的空间存放数组。

这里实现第二、三种,第一种并行效率太低了所以不考虑。

2.并行程序(c++实现)

第二种:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdbool.h>
#include "ctime"
#include "chrono"
#include <iostream>
#define N 200000000

using namespace std;
using namespace std::chrono;
double num[N];
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;
int count=1;

struct my_para{
    int left;
    int right;
};

void *quik_sort(void*arg){
    struct my_para *para;
    para=(struct my_para*)arg;
    int left=(*para).left;
    int right=(*para).right;
    if (right<=left){
        return NULL;
    }

    int start=left,end=right;
    double mid=0;
    while (left<right){
        while (left<right && num[right]>=num[start]){
            right-=1;
        }
        while (left<right && num[left]<=num[start]){
            left+=1;
        }
        mid=num[left];
        num[left]=num[right];
        num[right]=mid;
    }
    if(num[left]>num[start]){
        left-=1;
    }
    mid=num[start];
    num[start]=num[left];
    num[left]=mid;

    pthread_t pid;
    bool createT=false;
    pthread_mutex_lock(&lock);
    if(count>0){
        count--;
        createT=true;
    }
    pthread_mutex_unlock(&lock);

    struct my_para s_left;
    s_left.left=start;
    s_left.right=left-1;
    if(createT){
        pthread_create(&pid,NULL,quik_sort,&s_left);
    }else{
        quik_sort(&s_left);
    }
    struct my_para s_right;
    s_right.left=left+1;
    s_right.right=end;
    quik_sort(&s_right);

    if(createT){
        pthread_join(pid,NULL);
        pthread_mutex_lock(&lock);
        count++;
        pthread_mutex_unlock(&lock);
    }

    return NULL;
}
double my(){
    struct my_para para;
    para.left = 0;
    para.right=N-1;
    srand(1);//保持一致性
    
    for(int i=0;i<N;i++){
        num[i]=(double)rand()/RAND_MAX;
    }
    // Start measuring time
    auto start = system_clock::now();

    quik_sort(&para);

    // Stop measuring time and calculate the elapsed time
    auto end = system_clock::now();
    auto duration = duration_cast<microseconds>(end - start);

    for(int i=1;i<N;i++){
        if(num[i]<num[i-1])printf("wrong:%lf,idx:%d\n",num[i],i);
    }
    return double(duration.count()) * microseconds::period::num / microseconds::period::den;
}

int main(int argc,char *argv[])
{
    if(argc==1){
        count=1;
    }else if(argc==2){
        count=(int)*argv[1];
        count-=48;
    }

    double elapsed = 0;
    for(int i=0;i<10;i++){
        elapsed+=my();
    }
    printf("用时: %.3f 秒\n", elapsed/10);

    return 0;
}

第三种:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdbool.h>
#include "ctime"
#include "chrono"
#include <iostream>
#define N 2000*100000

using namespace std;
using namespace std::chrono;
double *num;
int count=1;

struct my_para{
    int left;
    int right;
};

void quik_sort(int start,int end){
    if (end<=start){
        return;
    }

    int left = start+1,right = end;
    double mid=0;
    while (left<right){
        while (left<right && num[right]>=num[start]){
            right-=1;
        }
        while (left<right && num[left]<num[start]){
            left+=1;
        }
        // swap(num[left], num[right]);
        mid=num[left];
        num[left]=num[right];
        num[right]=mid;
    }
    if(num[left]>num[start]){
        left-=1;
    }
    // swap(num[left], num[start]);
    mid=num[start];
    num[start]=num[left];
    num[left]=mid;

    quik_sort(start,left-1);
    
    quik_sort(left+1,end);

    return;
}


void *my_pthread(void*arg){
    struct my_para *para;
    para=(struct my_para*)arg;
    int left=(*para).left;
    int right=(*para).right;

    quik_sort(left,right);

    pthread_exit(NULL);
}

double my(){
    struct my_para *para = (struct my_para*)malloc(sizeof(struct my_para)*count);
    if(para==NULL){
        printf("para");
        return 0;
    }
    
    double *sortedNum = (double*)malloc(sizeof(double)*N);
    if(sortedNum==NULL){
        printf("sortedNum");
        return 0;
    }

    for(int i=0;i<N;i++){
        num[i]=(double)rand();
    }

    pthread_t pid[count];
    int numThread=N/count;

    auto start = system_clock::now();

    for(int i=0;i<count;i++){
        if(i==count-1){
            para[i].left = i*numThread;
            para[i].right=N-1;
        }else{
            para[i].left = i*numThread;
            para[i].right=(i+1)*numThread-1;
        }
        int status = pthread_create(&pid[i],NULL,my_pthread,&para[i]);
        if (status != 0) {
            printf("create thread error");
            return -1;
        }
    }

    int ind[count];
    for(int i=0;i<count;i++){
        pthread_join(pid[i],NULL);
        ind[i]=para[i].left;
    }

    double min_num=RAND_MAX;
    int mid_ind=0;
    for(int i=0;i<N;i++){
        min_num=RAND_MAX;
        for(int j=0;j<count;j++){
            if(ind[j]<=para[j].right){
                if(num[ind[j]]<min_num){
                    min_num=num[ind[j]];
                    mid_ind=j;
                }
            }
        }
        sortedNum[i]=min_num;
        ind[mid_ind]+=1;
    }


    // Stop measuring time and calculate the elapsed time
    auto end = system_clock::now();
    auto duration = duration_cast<microseconds>(end - start);

    for(int i=1;i<N;i++){//check if sort worked?
        if(sortedNum[i]<sortedNum[i-1])printf("wrong:%lf,idx:%d\n",sortedNum[i],i);
        // printf("ab?");
    }
    free(sortedNum);
    free(para);
    return double(duration.count()) * microseconds::period::num / microseconds::period::den;
}

int main(int argc,char *argv[])
{
    if(argc==1){
        count=1;
    }else if(argc==2){
        count=(int)*argv[1];
        count-=48;
    }

    srand(1);//保持一致性
    num = (double*)malloc(sizeof(double)*N);
    if(num==NULL){
        printf("num");
        return -1;
    }

    double elapsed = 0;
    for(int i=0;i<10;i++){
            elapsed+=my();
    }
    printf("用时: %.3f 秒\n", elapsed/10);
    free(num);
    return 0;
}

PS:

遇到了segmentation fault,就注释函数一个个查找原因,我这边是电脑内存不够,溢出了导致的,重启电脑就行。

参考:

pthread 快排 基础版_快速排序算法的pthread实现-CSDN博客

pthread实现快速排序_快速排序 并行 pthread-CSDN博客

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是基于pthread的线程池实现并行快速排序的C代码: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #define MAX_THREADS 16 #define MAX_SIZE 1024 int data[MAX_SIZE]; int n, m; struct arg_struct { int start; int end; }; void *quicksort(void *arg) { struct arg_struct *args = (struct arg_struct *) arg; int start = args->start; int end = args->end; int pivot = data[end]; int i = start - 1; int j, tmp; if (start >= end) { return NULL; } for (j = start; j < end; j++) { if (data[j] < pivot) { i++; tmp = data[i]; data[i] = data[j]; data[j] = tmp; } } tmp = data[i + 1]; data[i + 1] = data[end]; data[end] = tmp; struct arg_struct left_args = {start, i}; struct arg_struct right_args = {i + 2, end}; pthread_t left_thread, right_thread; if (m < MAX_THREADS - 1) { m += 2; pthread_create(&left_thread, NULL, quicksort, (void *) &left_args); pthread_create(&right_thread, NULL, quicksort, (void *) &right_args); pthread_join(left_thread, NULL); pthread_join(right_thread, NULL); m -= 2; } else { quicksort((void *) &left_args); quicksort((void *) &right_args); } return NULL; } int main() { int i; printf("Enter number of elements: "); scanf("%d", &n); printf("Enter elements: "); for (i = 0; i < n; i++) { scanf("%d", &data[i]); } struct arg_struct args = {0, n - 1}; m = 1; quicksort((void *) &args); printf("Sorted array: "); for (i = 0; i < n; i++) { printf("%d ", data[i]); } printf("\n"); return 0; } ``` 该程序通过输入一组数据,利用快速排序算法进行排序,利用pthread实现线程池,将排序任务分配给多个线程进行并行处理。程序中定义了一个结构体arg_struct用于传递给线程的参数,其中包括排序起点和终点。当线程池中线程数小于最大线程数时,创建两个新线程进行递归排序;否则,直接进行递归排序。最终输出排序结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值