简体   繁体   English

在C中用PThreads归并排序

[英]Merge Sort with PThreads in C

Side Note: I'm beginning to learn how to use pthreads and I'm starting to get the concept.旁注:我开始学习如何使用 pthreads 并且我开始了解这个概念。 I've been using this example script (written in C++) here to manage a Merge Sort with threads:https://www.geeksforgeeks.org/merge-sort-using-multi-threading/我一直在这里使用这个示例脚本(用 C++ 编写)来管理带线程的合并排序:https://www.geeksforgeeks.org/merge-sort-using-multi-threading/

Since I'm writing my own merge sort in C rather than C++, I rewrote this example script to test and noticed an issue.因为我在 C 而不是 C++ 中编写自己的合并排序,所以我重写了这个示例脚本来测试并注意到一个问题。 Rather than MAX 20 for an array of 20 elements, I decided to go with 15. I noticed that the sorting/merging is invalid (yet somewhat close) and I cannot figure out why... In addition, I've altered the code to use a different amount of threads instead of THREAD_MAX 4 so I may change it to 5 or 10 threads.对于 20 个元素的数组,我决定使用 go 而不是MAX 20和 15。我注意到排序/合并无效(但有点接近),我不知道为什么......此外,我已经改变了代码使用不同数量的线程而不是THREAD_MAX 4 ,因此我可以将其更改为 5 或 10 个线程。

Could it be the Merge that's yielding invalid results?可能是 Merge 产生了无效结果吗? I've commented it below within main().我在下面的 main() 中对其进行了评论。

My C++ to C convert below:我的C++转C如下:

#include <pthread.h>
#include <time.h>
#include <stdlib.h>

// number of elements in array
#define MAX 15

// number of threads
#define THREAD_MAX 4

//using namespace std;

// array of size MAX
int a[MAX];
int part = 0;

// merge function for merging two parts
void merge(int low, int mid, int high)
{   
    int* left = (int*) malloc( (mid - low + 1) * sizeof(int));
    int* right = (int*) malloc( (high - mid) * sizeof(int));


    // n1 is size of left part and n2 is size
    // of right part
    int n1 = mid - low + 1,
    n2 = high - mid,
    i, j;

    // storing values in left part
    for (i = 0; i < n1; i++)
        left[i] = a[i + low];

    // storing values in right part
    for (i = 0; i < n2; i++)
        right[i] = a[i + mid + 1];

    int k = low;
    i = j = 0;

    // merge left and right in ascending order
    while (i < n1 && j < n2) {
        if (left[i] <= right[j])
            a[k++] = left[i++];
        else
            a[k++] = right[j++];
    }

    // insert remaining values from left
    while (i < n1) {
        a[k++] = left[i++];
    }

    // insert remaining values from right
    while (j < n2) {
        a[k++] = right[j++];
    }

    free(left);
    free(right);
}

// merge sort function
void merge_sort(int low, int high)
{
    // calculating mid point of array
    int mid = low + (high - low) / 2;
    if (low < high) {

        // calling first half
        merge_sort(low, mid);

        // calling second half
        merge_sort(mid + 1, high);

        // merging the two halves
        merge(low, mid, high);
    }
}

// thread function for multi-threading
void* merge_sort123(void* arg)
{
    // which part out of 4 parts
    int thread_part = part++;

    // calculating low and high
    int low = thread_part * (MAX / THREAD_MAX);
    int high = (thread_part + 1) * (MAX / THREAD_MAX) - 1;

    // evaluating mid point
    int mid = low + (high - low) / 2;
    if (low < high) {
        merge_sort(low, mid);
        merge_sort(mid + 1, high);
        merge(low, mid, high);
    }
    return 0;
}



// Driver Code
int main()
{
    // generating random values in array
    for (int i = 0; i < MAX; i++){
        a[i] = rand() % 100;
    //        printf("%d ", a[i]);
    }


    pthread_t threads[THREAD_MAX];

    // creating 4 threads
    for (int i = 0; i < THREAD_MAX; i++)
        pthread_create(&threads[i], NULL, merge_sort123,
                       (void*)NULL);

    // joining all 4 threads
    for (int i = 0; i < THREAD_MAX; i++)
        pthread_join(threads[i], NULL);


    ///////////////////////////////////////////////////////////////
    // --- THIS MAY BE THE PART WHERE THE MERGING IS INVALID --- //
    ///////////////////////////////////////////////////////////////
    // merging the final 4 parts
    merge(0, (MAX / 2 - 1) / 2, MAX / 2 - 1);
    merge(MAX / 2, MAX/2 + (MAX-1-MAX/2)/2, MAX - 1);
    merge(0, (MAX - 1)/2, MAX - 1);


    // displaying sorted array
    printf("\n\nSorted array: ");
    for (int i = 0; i < MAX; i++)
        printf ("%d ", a[i]);


    printf("\n");
    return 0;
}

I found this Q&A useful, so I am reporting back my solution to the pthread sort problem.我发现此 Q&A 很有用,因此我将报告我对 pthread 排序问题的解决方案。 I used @CraigEstey's solution, but changed it to read ints from stdin and sort to stdout just like sort -n would work.我使用了@CraigEstey 的解决方案,但将其更改为从标准输入读取整数并排序到标准输出,就像sort -n一样。

Here is my nsort.c这是我的 nsort.c

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>

typedef size_t I;
#if 1
typedef int T;
#define CMP(x,y)  (x)<(y)
#define CPY(x)    (x)
#define PRINT(x)  printf("%d\n",x)
#define PARSE(x)  atoi(x)
#else
typedef char *T;
#define CMP(x,y) (strcmp(x,y)<0)
#define CPY(x)    strdup(x)
#define PRINT(x)  printf("%s",x)
#define PARSE(x)  strdup(x)
#endif
#define SIZE      sizeof(T)

T *a;
void merge(I l, I m, I h)
{
    I n1 = m - l + 1, n2 = h - m;
    T *L = malloc(n1 * SIZE);
    T *R = malloc(n2 * SIZE);
    I i, j, k = l;
    for (i = 0; i < n1; i++)
        L[i] = a[i + l];
    for (i = 0; i < n2; i++)
        R[i] = a[i + m + 1];
    i = j = 0;
    while (i < n1 && j < n2) {
    if (CMP(L[i], R[j]))
        a[k++] = L[i++];
    else
        a[k++] = R[j++];
    }
    while (i < n1)
        a[k++] = L[i++];
    while (j < n2)
        a[k++] = R[j++];
    free(L);
    free(R);
}

void merge_sort(I l, I h)
{
    I m = (l + h) / 2;
    if (l < h) {
      merge_sort(l, m);
      merge_sort(m + 1, h);
      merge(l, m, h);
    }
}

FILE *in = 0;
I MAX;
I readi()
{
    char b[99];  // LIMITATION: will read input only up to 99 chars
    MAX = 0;
    I m = 1024;
    assert(a = malloc(SIZE * m));
    while (fgets(b, 99, in)) {
      MAX++;
      if (MAX > m)
          assert(a = realloc(a, SIZE * (m <<= 1)));
      a[MAX - 1] = PARSE(b);
    }
    return MAX;
}

I writei()
{
    for (I i = 0; i < MAX; i++)
      PRINT(a[i]);
    return MAX;
}

struct tsk {
    I l, h;
};
void *merge_sort_thread(void *arg)
{
    struct tsk tsk = *(struct tsk *) arg;
    merge_sort(tsk.l, tsk.h);
    return 0;
}

int main(int c, char **v)
{
    struct tsk *tsk;
    assert( in = c == 2 ? fopen(v[1], "r") : stdin );
    I N = sysconf(_SC_NPROCESSORS_ONLN);
    assert(readi());
    if (MAX < N)
      N = 1;
    pthread_t threads[N];
    struct tsk tsklist[N];
    I p = MAX / N, l = 0;
    for (I i = 0; i < N; i++, l += p) {
      tsk = &tsklist[i];
      tsk->l = l;
      tsk->h = l + p - 1;
      if (i == (N - 1))
        tsk->h = MAX - 1;
    }
    for (I i = 0; i < N; i++)
      pthread_create(&threads[i], NULL, merge_sort_thread, &tsklist[i]);
    for (I i = 0; i < N; i++)
      pthread_join(threads[i], NULL);

    struct tsk *tskm = &tsklist[0];
    for (I i = 1; i < N; i++) {
      struct tsk *tsk = &tsklist[i];
      merge(tskm->l, tsk->l - 1, tsk->h);
    }
    return assert(writei()), 0;
}

I wrote a test and put it in a Makefile:我写了一个测试,放在一个Makefile中:

test: nsort
        MAXNUMBER=1100100 ./gen >in
        ./nsort <in >nsort.out
        sort -n  in >sort.out
        cmp -s sort.out nsort.out

nsort: nsort.c
        gcc -g nsort.c -o nsort -lpthread

With a script called gen:使用名为 gen 的脚本:

#!/usr/bin/awk -f
BEGIN {
  srand();
  i=1;
  n=ENVIRON["MAXNUMBER"]
  while (i++<=n) {
    printf("%d\n",int(rand()*n + 0.5));
  }
  exit;
}

I found this nsort to be faster than sort -n , but it didn't use much CPU.我发现这个 nsort 比sort -n更快,但它没有使用太多 CPU。

As I mentioned in the top comments, there are a few issues with the original code. 正如我在最高注释中提到的那样,原始代码存在一些问题。

The above race condition. 以上比赛条件。

The [seemingly] missing delete at the bottom of merge merge底部的[看似]缺失的delete

The fact that the number of array elements must be a multiple of the number of threads. 数组元素的数量必须是线程数量的倍数的事实。 If not, the range for the last thread will be calculated incorrectly. 否则,将错误地计算最后一个线程的范围。

The final merge in the main thread is fixed/hardwired for 4 threads. 主线程中的最终合并为4个线程固定/硬接线。


A general solution is possible. 通用的解决方案是可能的。 However, unless the array size is very large, it isn't as big a time saver, so it's mostly for practice with multithreading [which is what you wanted, I believe]. 但是,除非数组的大小很大,否则它不会节省时间,因此它主要是用于多线程的实践[我相信,这是您想要的]。 See: Multithreaded quicksort or mergesort 请参阅: 多线程快速排序或合并排序

It is easier to pass several parameters to a thread using a control struct. 使用控件结构将多个参数传递给线程更容易。 This is good technique for multithreading in general. 通常,这是用于多线程的好技术。

The main thread can prepopulate this with the array ranges for each thread. 主线程可以使用每个线程的数组范围对此进行预填充。 It can later use these control structs to generalize the final merge. 以后可以使用这些控制结构来概括最终合并。

Here's aa cleaned up version that works for an arbitrary array size and arbitrary number of threads: 这是一个清理版本,适用于任意数组大小和任意数量的线程:

#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>

int opt_a;
int opt_t;
int opt_r;

// number of elements in array
//#define MAX 15
//#define MAX 16
int MAX;

// number of threads
//#define THREAD_MAX 4
int THREAD_MAX;

//using namespace std;

// array of size MAX
int *a;

// thread control parameters
struct tsk {
    int tsk_no;
    int tsk_low;
    int tsk_high;
};

// merge function for merging two parts
void
merge(int low, int mid, int high)
{

    // n1 is size of left part and n2 is size of right part
    int n1 = mid - low + 1;
    int n2 = high - mid;

    int *left = malloc(n1 * sizeof(int));
    int *right = malloc(n2 * sizeof(int));

    int i;
    int j;

    // storing values in left part
    for (i = 0; i < n1; i++)
        left[i] = a[i + low];

    // storing values in right part
    for (i = 0; i < n2; i++)
        right[i] = a[i + mid + 1];

    int k = low;

    i = j = 0;

    // merge left and right in ascending order
    while (i < n1 && j < n2) {
        if (left[i] <= right[j])
            a[k++] = left[i++];
        else
            a[k++] = right[j++];
    }

    // insert remaining values from left
    while (i < n1)
        a[k++] = left[i++];

    // insert remaining values from right
    while (j < n2)
        a[k++] = right[j++];

    free(left);
    free(right);
}

// merge sort function
void
merge_sort(int low, int high)
{

    // calculating mid point of array
    int mid = low + (high - low) / 2;

    if (low < high) {
        // calling first half
        merge_sort(low, mid);

        // calling second half
        merge_sort(mid + 1, high);

        // merging the two halves
        merge(low, mid, high);
    }
}

// thread function for multi-threading
void *
merge_sort123(void *arg)
{
    struct tsk *tsk = arg;
    int low;
    int high;

    // calculating low and high
    low = tsk->tsk_low;
    high = tsk->tsk_high;

    // evaluating mid point
    int mid = low + (high - low) / 2;

    if (low < high) {
        merge_sort(low, mid);
        merge_sort(mid + 1, high);
        merge(low, mid, high);
    }

    return 0;
}

// Driver Code
int
main(int argc, char **argv)
{
    char *cp;
    struct tsk *tsk;

    --argc;
    ++argv;

    MAX = 15;
    THREAD_MAX = 4;

    // use new/general algorithm by default
    opt_a = 1;

    for (; argc > 0; --argc, ++argv) {
        cp = *argv;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        case 'M':  // array count
            MAX = atoi(cp + 2);
            break;

        case 'T':  // thread count
            THREAD_MAX = atoi(cp + 2);
            break;

        case 'a':  // change algorithm
            opt_a = !opt_a;
            break;

        case 'r':  // do _not_ use rand -- use linear increment
            opt_r = !opt_r;
            break;

        case 't':  // tracing
            opt_t = !opt_t;
            break;

        default:
            break;
        }
    }

    // allocate the array
    a = malloc(sizeof(int) * MAX);

    // generating random values in array
    if (opt_t)
        printf("ORIG:");
    for (int i = 0; i < MAX; i++) {
        if (opt_r)
            a[i] = MAX - i;
        else
            a[i] = rand() % 100;
        if (opt_t)
            printf(" %d", a[i]);
    }
    if (opt_t)
        printf("\n");

    pthread_t threads[THREAD_MAX];
    struct tsk tsklist[THREAD_MAX];

    int len = MAX / THREAD_MAX;

    if (opt_t)
        printf("THREADS:%d MAX:%d LEN:%d\n", THREAD_MAX, MAX, len);

    int low = 0;

    for (int i = 0; i < THREAD_MAX; i++, low += len) {
        tsk = &tsklist[i];
        tsk->tsk_no = i;

        if (opt_a) {
            tsk->tsk_low = low;
            tsk->tsk_high = low + len - 1;
            if (i == (THREAD_MAX - 1))
                tsk->tsk_high = MAX - 1;
        }

        else {
            tsk->tsk_low = i * (MAX / THREAD_MAX);
            tsk->tsk_high = (i + 1) * (MAX / THREAD_MAX) - 1;
        }

        if (opt_t)
            printf("RANGE %d: %d %d\n", i, tsk->tsk_low, tsk->tsk_high);
    }

    // creating 4 threads
    for (int i = 0; i < THREAD_MAX; i++) {
        tsk = &tsklist[i];
        pthread_create(&threads[i], NULL, merge_sort123, tsk);
    }

    // joining all 4 threads
    for (int i = 0; i < THREAD_MAX; i++)
        pthread_join(threads[i], NULL);

    // show the array values for each thread
    if (opt_t) {
        for (int i = 0; i < THREAD_MAX; i++) {
            tsk = &tsklist[i];
            printf("SUB %d:", tsk->tsk_no);
            for (int j = tsk->tsk_low; j <= tsk->tsk_high; ++j)
                printf(" %d", a[j]);
            printf("\n");
        }
    }

    // merging the final 4 parts
    if (opt_a) {
        struct tsk *tskm = &tsklist[0];
        for (int i = 1; i < THREAD_MAX; i++) {
            struct tsk *tsk = &tsklist[i];
            merge(tskm->tsk_low, tsk->tsk_low - 1, tsk->tsk_high);
        }
    }
    else {
        merge(0, (MAX / 2 - 1) / 2, MAX / 2 - 1);
        merge(MAX / 2, MAX / 2 + (MAX - 1 - MAX / 2) / 2, MAX - 1);
        merge(0, (MAX - 1) / 2, MAX - 1);
    }

    // displaying sorted array
    printf("\n\nSorted array:");
    for (int i = 0; i < MAX; i++)
        printf(" %d", a[i]);
    printf("\n");

    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM