繁体   English   中英

多线程排序应用

[英]Multithreaded Sorting Application

我是多线程编程的新手,所以我想我会从事一个项目来帮助我学习它。 以下是该项目的详细信息:

用 c 编写一个多线程排序程序,其工作方式如下:一个整数列表被分成两个大小相等的较小列表。 两个独立的线程(我们将其称为排序线程)使用您选择的排序算法对每个子列表进行排序。 然后,这两个子列表由第三个线程(合并线程)合并,该线程将两个子列表合并为一个已排序的列表。

//Sort a list of numbers using two separate threads
//by sorting half of each list separately then
//recombining the lists

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

#define SIZE 10
#define NUMBER_OF_THREADS 3

void *sorter(void *params);    /* thread that performs basic sorting algorithm */
void *merger(void *params);    /* thread that performs merging of results */

int list[SIZE] = {7,12,19,3,18,4,2,6,15,8};

int result[SIZE];

typedef struct
{
    int from_index;
    int to_index;
} parameters;

int main (int argc, const char * argv[])
{
    int i;

    pthread_t workers[NUMBER_OF_THREADS];

    /* establish the first sorting thread */
    parameters *data = (parameters *) malloc (sizeof(parameters));
    data->from_index = 0;
    data->to_index = (SIZE/2) - 1;
    pthread_create(&workers[0], 0, sorter, data);

    /* establish the second sorting thread */
    data = (parameters *) malloc (sizeof(parameters));
    data->from_index = (SIZE/2);
    data->to_index = SIZE - 1;
    pthread_create(&workers[1], 0, sorter, data);

    /* now wait for the 2 sorting threads to finish */
    for (i = 0; i < NUMBER_OF_THREADS - 1; i++)
        pthread_join(workers[i], NULL);

    /* establish the merge thread */
    data = (parameters *) malloc(sizeof(parameters));
    data->from_index = 0;
    data->to_index = (SIZE/2);
    pthread_create(&workers[2], 0, merger, data);

    /* wait for the merge thread to finish */
    pthread_join(workers[2], NULL);


    /* output the sorted array */

    return 0;
}

void *sorter(void *params)
{
    parameters* p = (parameters *)params;

    //SORT

    int begin = p->from_index;
    int end = p->to_index+1;

    int z;
    for(z = begin; z < end; z++){
        printf("The array recieved is: %d\n", list[z]);
    }

    printf("\n");

    int i,j,t,k;

    for(i=begin; i< end; i++)
    {
        for(j=begin; j< end-i-1; j++)
        {

            if(list[j] > list[j+1])
            {
                t = list[j];
                list[j] = list[j+1];
                list[j+1] = t;

            }
        }
    }

    for(k = begin; k< end; k++){
            printf("The sorted array: %d\n", list[k]);
    }

    int x;
    for(x=begin; x<end; x++)
    {
            list[x] = result[x];
    }

    printf("\n");

    pthread_exit(0);
}

void *merger(void *params)
{
    parameters* p = (parameters *)params;

   //MERGE
    int begin = p->from_index;
    int end = p->to_index+1;

    int i,j,t;

    printf("list[1]: %d",list[1]);
    printf("result[1]: %d",result[1]);

    for(i=begin; i< end; i++)
    {
        for(j=begin; j< end-i; j++)
        {

            if(result[j] > result[j+1])
            {
                t = result[j];
                result[j] = result[j+1];
                result[j+1] = t;

            }
        }
    }

    int d;
    for(d=0; d<SIZE; d++)
    {
        printf("The final resulting array is: %d\n", result[d]);
    }

    pthread_exit(0);
}

我不确定我的算法做错了什么,它不起作用。 它似乎没有捕捉到新的排序数组。 对这个问题的任何帮助将不胜感激! 再次提前感谢您的帮助!

你的做法是错误的。 您应该拆分分区,然后递归或线程化到它们中,加入结果,然后合并。 相信我,很容易搞砸这个算法。

首先,请确保您的合并算法是可靠的 如果您的合并在单线程领域出现问题,添加线程只会让情况变得更糟。 在您的情况下,您使情况变得更糟,因为您的合并线程似乎与排序器线程同时运行。

也就是说,退一步考虑一下。 Mergesort 是关于分而治之的。 要进行合并排序,您应该执行以下操作:

  • 建立最大线程数。 相信我,您最不希望发生的事情是为每个分区旋转一个线程。 如果您足够努力地计算数学,则 1024 个值的序列有 1023 个分区。 那么多线程不是解决方案。 建立一些界限。

  • 建立您愿意为其旋转线程的最小分区大小。 这与上面的第一项一样重要。 就像您不想旋转 1023 个线程来对 1024 个插槽的序列进行排序一样,您也不希望旋转一个线程只是为了对具有两个项目的序列进行排序。 收益为零,成本高。

  • 有一个可靠的合并算法。 有很多有效的方法可以做到这一点,但先做一些简单的事情,然后再进行改进。 现在,我们只对正确处理一般线程感兴趣。 总是有时间用一个奇特的合并算法来增强它(比如就地,相信我比听起来更难)。

有了上面的想法是这样的:

  • 归并排序算法将具有三个参数:起始指针、长度和线程深度。 出于我们的目的,在我们最多使用 2N-1 个螺纹的情况下,螺纹深度将为 N。 (稍后会详细介绍,但相信我,这样计算会更容易)。

  • 如果线程深度已达到零或序列长度低于最小阈值*我们设置),则不要设置和运行新线程。 只需再次递归到我们的函数中。

  • 否则,拆分分区。 设置一个包含分区定义的结构(对我们来说这将是一个起点和一个长度以及将是 N/2 的线程深度),使用该参数块启动一个线程,然后启动另一个线程。 而是使用当前线程为“另一半”递归到 merge_sort_mt() 中。

  • 一旦当前线程从其递归返回,必须通过连接等待另一个线程。 一旦完成,两个分区都将完成,并且可以使用您的简单合并算法合并它们。

哇。 好的。 那么它在实践中的表现如何:

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


struct Params
{
    int *start;
    size_t len;
    int depth;
};

// only used for synchronizing stdout from overlap.
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

// forward declare our thread proc
void *merge_sort_thread(void *pv);


// a simple merge algorithm. there are *several* more efficient ways
//  of doing this, but the purpose of this exercise is to establish
//  merge-threading, so we stick with simple for now.
void merge(int *start, int *mid, int *end)
{
    int *res = malloc((end - start)*sizeof(*res));
    int *lhs = start, *rhs = mid, *dst = res;

    while (lhs != mid && rhs != end)
        *dst++ = (*lhs < *rhs) ? *lhs++ : *rhs++;

    while (lhs != mid)
        *dst++ = *lhs++;

    // copy results
    memcpy(start, res, (rhs - start) * sizeof *res);
    free(res);
}

// our multi-threaded entry point.
void merge_sort_mt(int *start, size_t len, int depth)
{
    if (len < 2)
        return;

    if (depth <= 0 || len < 4)
    {
        merge_sort_mt(start, len/2, 0);
        merge_sort_mt(start+len/2, len-len/2, 0);
    }
    else
    {
        struct Params params = { start, len/2, depth/2 };
        pthread_t thrd;

        pthread_mutex_lock(&mtx);
        printf("Starting subthread...\n");
        pthread_mutex_unlock(&mtx);

        // create our thread
        pthread_create(&thrd, NULL, merge_sort_thread, &params);

        // recurse into our top-end parition
        merge_sort_mt(start+len/2, len-len/2, depth/2);

        // join on the launched thread
        pthread_join(thrd, NULL);

        pthread_mutex_lock(&mtx);
        printf("Finished subthread.\n");
        pthread_mutex_unlock(&mtx);
    }

    // merge the partitions.
    merge(start, start+len/2, start+len);
}

// our thread-proc that invokes merge_sort. this just passes the
//  given parameters off to our merge_sort algorithm
void *merge_sort_thread(void *pv)
{
    struct Params *params = pv;
    merge_sort_mt(params->start, params->len, params->depth);
    return pv;
}

// public-facing api
void merge_sort(int *start, size_t len)
{
    merge_sort_mt(start, len, 4); // 4 is a nice number, will use 7 threads.
}

int main()
{
    static const unsigned int N = 2048;
    int *data = malloc(N * sizeof(*data));
    unsigned int i;

    srand((unsigned)time(0));
    for (i=0; i<N; ++i)
    {
        data[i] = rand() % 1024;
        printf("%4d ", data[i]);
        if ((i+1)%8 == 0)
            printf("\n");
    }
    printf("\n");

    // invoke our multi-threaded merge-sort
    merge_sort(data, N);
    for (i=0; i<N; ++i)
    {
        printf("%4d ", data[i]);
        if ((i+1)%8 == 0)
            printf("\n");
    }
    printf("\n");

    free(data);

    return 0;

}

这个输出看起来像这样:

 825  405  691  290  900  715  125  969 
 534  809  783  820  933  895  310  687 
 152   19  659  856   46  765  497  371 
 339  660  297  509  152  796  230  465 
 502  948  278  317  144  941  195  208 
 617  428  118  505  719  161   53  292 
 ....
 994  154  745  666  590  356  894  741 
 881  129  439  237   83  181   33  310 
 549  484   12  524  753  820  443  275 
  17  731  825  709  725  663  647  257 

Starting subthread...
Starting subthread...
Starting subthread...
Starting subthread...
Starting subthread...
Starting subthread...
Starting subthread...
Finished subthread.
Finished subthread.
Finished subthread.
Finished subthread.
Finished subthread.
Finished subthread.
Finished subthread.
   0    0    1    1    1    2    3    3 
   5    5    5    5    6    6    7    7 
   7    7    7    8    8   10   10   11 
  11   11   12   12   12   13   14   14 
  15   15   15   15   16   17   17   17 
  17   18   18   19   19   19   20   21 
  21   21   22   22   23   24   24   24 
  25   25   25   26   26   28   28   29 
  29   29   30   30   30   30   30   31 
....
 994  995  996  998 1000 1001 1001 1003 
1003 1003 1003 1004 1004 1005 1007 1007 
1010 1010 1010 1010 1011 1012 1012 1012 
1012 1013 1013 1013 1015 1015 1016 1016 
1016 1017 1018 1019 1019 1019 1020 1020 
1020 1021 1021 1021 1021 1022 1023 1023 

其中最重要的部分是限制器,它使我们不会陷入线程疯狂(使用递归线程算法很容易意外地做到这一点),以及在将它们的内容与分区的另一半​​(我们在我们的线程上排序,并且可能也做了同样的事情)。

这是一个有趣的练习,我希望你能从中有所收获。 祝你好运。


更新:集成qsort()

一个有趣的任务是使用qsort()执行此功能以对较小的分区进行排序或在线程池耗尽时执行此功能。 qsort()是为这个聚会带来的一个相当大的锤子,因此您将希望将最小分区大小提高到一些值得尊重的程度(在下面的示例中,我们使用 256 个元素)。

那么将qsort()集成到子分区而不是手动合并排序需要什么? 令人惊讶的是,并不多。 qsort()兼容比较器开始:

// comparator for qsort
int cmp_proc(const void *arg1, const void* arg2)
{
    const int *lhs = arg1;
    const int *rhs = arg2;
    return (*lhs < *rhs) ? -1 : (*rhs < *lhs ? 1 : 0);
}

真是脑残。 现在,将 mt-wrapper 修改为如下所示:

// our multi-threaded entry point.
void merge_sort_mt(int *start, size_t len, int depth)
{
    if (len < 2)
        return;

    // invoke qsort on the partition. no need for merge
    if (depth <= 0 || len <= 256)
    {
        qsort(start, len, sizeof(*start), cmp_proc);
        return;
    }

    struct Params params = { start, len/2, depth/2 };
    pthread_t thrd;

    pthread_mutex_lock(&mtx);
    printf("Starting subthread...\n");
    pthread_mutex_unlock(&mtx);

    // create our thread
    pthread_create(&thrd, NULL, merge_sort_thread, &params);

    // recurse into our top-end parition
    merge_sort_mt(start+len/2, len-len/2, depth/2);

    // join on the launched thread
    pthread_join(thrd, NULL);

    pthread_mutex_lock(&mtx);
    printf("Finished subthread.\n");
    pthread_mutex_unlock(&mtx);

    // merge the paritions.
    merge(start, start+len/2, start+len);
}

就是这样。 认真的 这就是全部。 证明这个工作是对原始程序的简单测试运行,如下所示:

 986  774   60  596  832  171  659  753 
 638  680  973  352  340  221  836  390 
 930   38  564  277  544  785  795  451 
  94  602  724  154  752  381  433  990 
 539  587  194  963  558  797  800  355 
 420  376  501  429  203  470  670  683 
....
 216  748  534  482  217  178  541  242 
 118  421  457  810   14  544  100  388 
 291   29  562  718  534  243  322  187 
 502  203  912  717 1018  749  742  430 
 172  831  341  331  914  866  931  368 

Starting subthread...
Starting subthread...
Starting subthread...
Starting subthread...
Starting subthread...
Starting subthread...
Starting subthread...
Finished subthread.
Finished subthread.
Finished subthread.
Finished subthread.
Finished subthread.
Finished subthread.
Finished subthread.
   0    0    1    1    1    1    3    3 
   3    4    5    5    6    6    6    6 
   7    7    8    9   10   10   10   10 
  11   12   12   12   13   13   14   14 
  14   15   15   15   16   17   17   19 
  19   20   20   21   21   21   22   22 
  23   23   23   24   24   24   25   26 
  26   26   26   27   28   28   28   28 
....
1000 1000 1000 1001 1001 1002 1003 1003 
1004 1004 1004 1005 1005 1005 1006 1007 
1008 1010 1010 1010 1010 1010 1011 1011 
1011 1012 1012 1012 1012 1013 1013 1013 
1015 1015 1015 1016 1016 1017 1017 1017 
1018 1018 1018 1019 1019 1021 1021 1022 

如您所见,结果是相似的。

几个问题:

1 - 你认为这段代码在做什么:

int x;
for(x=begin; x<end; x++)
{
        list[x] = result[x];
}

2 - 您的合并目前看起来与您的分拣机完全一样。 相反,它应该将列表前半部分和列表后半部分的排序值合并到结果中。

你的代码是正确的,我已经修改了你的代码并试图找出错误,循环索引没有正确映射,你在一个循环中将空结果列表分配到实际数据中,所以列表取零。

在下面找到修改后的代码和输出。

//Sort a list of numbers using two separate threads
//by sorting half of each list separately then
//recombining the lists



void *sort(void *params)
{
    parameters* p = (parameters *)params;

    //SORT

    int begin = p->fromVal;
    int end = p->toVal+1;

    for(int i = begin; i < end; i++){
        printf("The array recieved is: %d\n", list[i]);
    }

    printf("\n");

    int temp=0;

    for(int i=begin; i< end; i++)
    {
        for(int j=begin; j< end-1; j++)
        {

            if(list[j] > list[j+1])
            {
                temp = list[j];
                list[j] = list[j+1];
                list[j+1] = temp;

            }
        }
    }

    for(int k = begin; k< end; k++){
            printf("The sorted array: %d\n", list[k]);
    }

    for(int i=begin; i<end; i++)
    {
            result[i] = list[i];
    }
    printf("\n");

    pthread_exit(NULL);
}

void *merging(void *params)
{
    parameters* p = (parameters *)params;

   //MERGE
    int begin = p->fromVal;
    int end = p->toVal+1;

    int temp;
    for(int i=begin; i< end; i++)
    {
        for(int j=begin; j< end-1; j++)
        {

            if(result[j] > result[j+1])
            {
                temp= result[j];
                result[j] = result[j+1];
                result[j+1] = temp;

            }
        }
    }
    printf("\n\nFINAL RESULT IS:\n");
    for(int d=begin+1; d<end; d++)
    {
        printf("The final resulting array is: %d\n", result[d]);
    }

    pthread_exit(NULL);
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>


/*globle variables*/
/* structure for passing data to threads */

typedef struct
{
    int *start;
    int end;
    int size;
} parameters;


int t = 1;
int *arr1, *arr2;
//for using quicksort
int comparator (const void * a, const void * b) {
   return ( *(int*)a - *(int*)b );
}

void *merge(void *params){
     //get data
   int *len = params;
    //SORT
    int start  = 0;
    int end = *len/2;
    int counter = end;
    int size =  *len;
    int index = 0;
    while (start < end && counter < size)
       {

          if (arr1[start] < arr1[counter])
          {

              arr2[index] = arr1[start];

            start ++;

          }
          else
          {


            arr2[index] = arr1[counter];

            counter ++;
         }
        index ++;
       }

       /* Copy the remaining elements , if there
       are any */

       while ( start < end)
       {

           arr2[index] = arr1[start];

          start ++;
          index ++;
       }

      /* Copy the remaining elements  , if there
       are any */
        while ( counter < size)
       {
          arr2[index] = arr1[counter];
          counter ++;
          index ++;
       }



}
void *sorting_thread(void *params){
    printf("Thread %d ......\n", t);
    t++;

    //get data
   parameters* data = (parameters *)params;
   //SORT

    int end = data->end;
    int size = data->size;


    //qsort
    qsort(data->start, end, sizeof(*data->start), comparator);
    printf("The array after sort : \n");
          for(int i = size - end; i < size; i ++){
               printf("arr1[%d]:%d, \n", i,arr1[i]);
         }
    printf("\n");
    pthread_exit(0);

}


void *merge_sort_thread(void *params){
    int *len = params;
    //varaible allocation for two sorting threads.
    parameters *data = (parameters *) malloc (sizeof(parameters));
    parameters *data1 = (parameters *) malloc (sizeof(parameters));
    if(data == NULL&& data1 == NULL){
      printf("Memory not allocated. \n");
      exit(0);
     }
     //value for data passing

         data->start= arr1;
         data->end = *len/2;
         data->size = *len/2;
         data1->start = arr1 + *len/2;
         data1->end = *len-*len/2;
         data1->size = *len;

    pthread_t left, right;/* the thread identifier */
     printf("Entering merge_Sorting..\n");

         /* create the sorting thread */

             pthread_create(&left, NULL, sorting_thread, data);

             pthread_create(&right, NULL, sorting_thread, data1);

          /* wait for the thread to exit */
             pthread_join(left, NULL);
             //free memory
             free(data);

             pthread_join(right, NULL);
        printf("Merging Thread %d ......\n", t);
         merge(len);
         printf("Process is done.\n");
         printf("The final output: \n");
         for(int i = 0; i < *len; i ++){
             if(i%10==0){
                 printf("\n");
             }
             printf("%d, ", arr2[i]);
         }
         printf("\n");
         //free memory
         free(data1);

         pthread_exit(0);

}



int main( int argc, char *argv[] )  {
    long len;
    int temp, c, j, k;
    char *ptr;

    //

   //check if the right amount of argument
   if( argc == 2 ) {
      printf("The input array size is %s\n", argv[1]);


        //covert the user input to integer

      len = strtol(argv[1], &ptr, 10);

   //check if the input is valid.
       if(len == 0) {//if not, leave the program.
            printf("Please enter a proper number. Leaving the program...\n");
       }else{

     //dynamically allocate memory
     arr1 = (int*)malloc(len * sizeof(int));
     arr2 = (int*)malloc(len * sizeof(int));
     //check Memory
     if(arr1 == NULL && arr2 == NULL){
      printf("Memory not allocated. \n");
      exit(0);
     }

         printf("Memory allocated. \n");
         //decide the value of data.


         //generate random number to 100
         srand(time(0));

         printf("The array before sorting is:  \n");

         for(int i = 0; i < len; i ++){
             arr1[i] = rand() % 100;
             if(i%10==0){
                 printf("\n");
             }
             printf("%d, ", arr1[i]);
         }
         printf(" \n");
         //merge sort handle all the threads
         pthread_t tid;/* the thread identifier */

         /* create the parent sorting thread */
         pthread_create(&tid, NULL, merge_sort_thread, &len);
         //wait for children thread
         pthread_join(tid, NULL);
          //printout array after merging threading
         printf("\nThe program is finished. \n");
         //free memory space
         free(arr2);
         free(arr1);
      }

   }
   else if( argc > 2 ) {
      printf("Too many arguments supplied.\n");
   }
   else {
      printf("One argument expected.\n");
   }

return 0;

}

暂无
暂无

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

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