多线程排序应用程序

时间:2014-05-08 02:18:11

标签: c multithreading

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

在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);
}

我不确定我在算法中做错了什么,它不起作用。它似乎没有捕获新的排序数组。任何关于这个问题的帮助都会非常感激!再次感谢你的帮助!

4 个答案:

答案 0 :(得分:15)

您的方法不正确。您应该拆分分区,然后递归或线程化它们,加入结果,然后合并。相信我,很容易搞砸这个算法。

在其他任何事情之前,让确定您的合并算法是 solid 。如果您的合并在单线程领域出现问题,那么添加线程只会让情况变得更糟。在您的情况下,您的情况会变得更糟,因为您的合并线程似乎与您的分拣机线程同时运行。

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

  • 建立最大个线程数。相信我,你想要发生的最后一件事是为每个分区旋转一个线程。如果你足够努力地处理数学,1024个值的序列有1023个分区。许多线程解决方案。建立一些界限。

  • 建立您愿意为其创建线程的最小分区大小。这与上面的第一项同样重要。就像你不想旋转1023个线程来排序1024个插槽的序列一样,你也不想旋转一个线程只是为了对一个两个的序列进行排序项目。没有任何好处和成本。

  • 拥有实体合并算法。有很多有效的方法可以做到这一点,但做一些事情简单并在以后加强它。现在,我们只是对通用线程正确感兴趣。总是有时间通过​​花哨的合并算法来增强它(就像就地,相信我比听起来更难)。

拥有上述想法是这样的:

  • 合并排序算法将有三个参数:起始指针,长度和线程深度。对于我们的目的,在我们使用最多2N-1个线程的情况下,线程深度将为N. (稍后会详细介绍,但请相信我,这样就可以更容易地进行数学运算)。

  • 如果线程深度达到零或序列长度低于我们设置的最小阈值*,请执行 not 设置并运行新线程。只需再次进入我们的功能。

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

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

呼。好。那么它在实践中看起来如何:

#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 

最重要的部分是限制器阻止我们进行线程狂放( easy 意外地使用递归线程算法),以及在合并其内容之前连接线程与分区的另一半​​(我们在我们的线程上排序,也可能做同样的事情)。

这是一项有趣的运动,我希望你能从中得到一些东西。祝你好运。


更新:整合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 :(得分:0)

有几个问题:

1 - 您认为此代码的作用如何:

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

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

答案 2 :(得分:0)

您的代码是正确的我修改了您的代码并试图找出错误, 循环索引未正确映射,您在一个循环中将空结果列表分配到实际数据中,因此列表为零。

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

//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);
}

答案 3 :(得分:0)

#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;

}