Боковое примечание: я начинаю изучать, как использовать pthreads, и начинаю понимать концепцию. Я использовал этот пример сценария (написанный на C++) здесь для управления сортировкой слиянием с потоками: https://www.geeksforgeeks.org/merge-sort-using-multi-threading/
Поскольку я пишу свою собственную сортировку слиянием на C, а не на C++, я переписал этот пример сценария для тестирования и заметил проблему. Вместо MAX 20 для массива из 20 элементов я решил использовать 15. Я заметил, что сортировка / объединение недействительны (но несколько близко), и я не могу понять, почему ... Кроме того, я изменил код на используйте другое количество потоков вместо THREAD_MAX 4, поэтому я могу изменить его на 5 или 10 потоков.
Может быть, слияние дает недопустимые результаты? Я прокомментировал это ниже в main ().
Мой преобразование 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;
}
@CraigEstey К сожалению, эти изменения приводят к тем же результатам. pthread_create(&threads[i], NULL, merge_sort123, (void *) i); вместо pthread_create(&threads[i], NULL, merge_sort123, (void*)NULL); и unsigned long thread_part = (unsigned long) arg; вместо int thread_part = part++;
Да, это не исправит логическую ошибку, только состояние гонки. Порт на C - это просто замена операторов new на вызовы malloc в merge. И замените delete в конце на free() [что вы сделали]. AFAICT, в связанном примере должен быть delete для каждого временного массива [если они не добавляются компилятором, когда функция выходит за пределы области видимости], поэтому меня беспокоит качество кода. Итак, меня интересует оригинал - он действительно работает? Фактически, я вытащил оригинал и запустил его с 15, и он не работает, так что не ваша ошибка.
Похоже, что MAX должен быть кратным THREAD_MAX? Я бы нашел лучший пример для начала, поскольку многопоточная версия должна иметь возможность обрабатывать массив произвольной длины, как и однопоточная версия. Чтобы решить, правильны ли окончательные слияния в main, перед их выполнением сначала распечатайте отдельные секции для каждого потока (т.е. они [все 4 из них] должны быть все в сортировке). Сделайте это с MAX как (например, 15) и 20. Вместо rand я бы создал обратный исходный массив: n - 1, n - 2, ..., 3, 2, 1. Легче определить, что пошло не так, и не наступит ли какая-то нить на чужое пространство.
@CraigEstey Спасибо за поддержку. Я надеюсь, что есть лучший пример, но похоже, что они не существуют в C, а в C++ используются разные методы, которые излишни для преобразования обратно в C. Все, что я делаю, чтобы понять это сейчас, - это возиться с этим скриптом.





Как я уже упоминал в верхних комментариях, с исходным кодом есть несколько проблем.
Вышеупомянутое состояние гонки.
[По-видимому] отсутствующий delete внизу merge
Дело в том, что количество элементов массива должен должно быть кратно количеству потоков. В противном случае диапазон для последнего потока будет рассчитан неверно.
Окончательное слияние в основном потоке фиксировано / зашито для 4 потоков.
Возможно общее решение. Однако, если размер массива не очень велик, это не так сильно экономит время, поэтому это в основном для практики с многопоточностью [как я полагаю, это то, что вы хотели]. См .: Многопоточная быстрая сортировка или сортировка слиянием
Проще передать потоку несколько параметров с помощью управляющей структуры. Это хороший метод для многопоточности в целом.
Основной поток может предварительно заполнить это диапазонами массива для каждого потока. Позже он может использовать эти управляющие структуры для обобщения окончательного слияния.
Вот очищенная версия, которая работает для произвольного размера массива и произвольного количества потоков:
#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;
}
Для начала у вас есть состояние гонки с
int thread_part = part++между потоками (т.е. некоторые потоки могут получить одно и то же число или может быть разрыв, когда данный сегмент покрыт нет). Попросите поток главный присвоить номер потока и передать его в качестве аргумента потоку: замените последний аргументpthread_createна:(void *) iи в функции потока:unsigned long thread_part = (unsigned long) arg;Я понимаю, что вы копируете эту часть кода, но пример, который вы связано с этим - и это является ошибка.