[英]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, ¶ms);
// 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, ¶ms);
// 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.