我是多线程编程的新手,所以我想我会参与一个项目来帮助我学习它。以下是该项目的详细信息:
在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);
}
我不确定我在算法中做错了什么,它不起作用。它似乎没有捕获新的排序数组。任何关于这个问题的帮助都会非常感激!再次感谢你的帮助!
答案 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, ¶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
最重要的部分是限制器阻止我们进行线程狂放( 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, ¶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 :(得分: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;
}