Я использую pthreads в C, чтобы выполнить две операции с массивом int: одна операция удваивает значение ячейки, другая операция уменьшает вдвое значение ячейки. Если после удвоения ячейки ее значение станет больше максимально допустимого значения, потоку необходимо дождаться, пока другой поток не уменьшит вдвое значение этой ячейки. Я инициализировал массив следующим образом: первые 5 ячеек имеют значение, очень близкое к разрешенному максимуму, а остальные пять имеют значение далеко от максимума.
Я решил использовать для этого глобальный мьютекс и условную переменную. В main сначала создается 10 удвоенных потоков, а затем еще 10 половинных потоков. Но потом моя программа зависает. Я не могу понять, в чем проблема, любая помощь приветствуется.
Моя мотивация - лучше понять потоки pthread и условные переменные.
Это код:
#include <stdio.h>
#include <stdlib.h>
#include <ntsid.h>
#include <pthread.h>
#include <unistd.h>
#define MAX 20
#define THREADS_NUM 10
#define OFFSET 10
typedef struct myStruct {
int cellId;
} myStruct;
int * cells;
pthread_mutex_t globalMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t globalCond = PTHREAD_COND_INITIALIZER;
pthread_t threads[THREADS_NUM * 2];
void * DoublerThread(void * arg) {
myStruct * myStr = (myStruct *) arg;
int id = myStr->cellId;
pthread_mutex_t mutex = globalMutex;
pthread_cond_t condition = globalCond;
pthread_mutex_lock(&mutex);
while((cells[id] * 2) > MAX) {
printf("Waiting... id = %d\n", id);
pthread_cond_wait(&condition, &mutex);
}
cells[id] *= 2;
printf("new val = %d, id = %d\n", cells[id], id);
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
void * HalverThread(void * arg) {
myStruct * myStr = (myStruct *) arg;
int id = myStr->cellId;
pthread_mutex_t mutex = globalMutex;
pthread_cond_t condition = globalCond;
sleep(1);
pthread_mutex_lock(&mutex);
cells[id] /= 2;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
void initMyStructs(myStruct ** myStructs) {
int i;
for(i = 0; i < THREADS_NUM * 2; i++) {
myStructs[i] = (myStruct *) malloc(sizeof(myStruct) * 2);
if (!myStructs[i]) {
printf("malloc error\n");
exit(EXIT_FAILURE);
}
myStructs[i]->cellId = i % THREADS_NUM;
}
}
void initCells() {
int i, tmp;
cells =(int *) malloc(sizeof(int));
if (!cells) {
printf("malloc error\n");
exit(EXIT_FAILURE);
}
for(i = 0; i <= THREADS_NUM; i++) {
if (i < THREADS_NUM / 2) {
cells[i] = MAX - 1;
} else {
tmp = cells[i] = 1;
}
}
}
int main() {
int i;
myStruct ** myStructs;
initMyStructs(myStructs);
initCells();
//create 10 Doubler threads
for(i = 0; i < THREADS_NUM; i++) {
pthread_create(&threads[i], NULL, DoublerThread, (void *) myStructs[i]);
}
//create 10 Halver threads
for(i = 0; i < THREADS_NUM; i++) {
pthread_create(&threads[i + OFFSET], NULL, HalverThread, (void *) myStructs[i + OFFSET]);
}
for(i = 0; i < THREADS_NUM + OFFSET; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
@Yunnosch Я добавил мотивировку проблемы, не уверен, что вы имеете в виду "array init", у меня есть функция initCells.
Приведите пример значений массивов и то, как они развиваются с течением времени под влиянием описываемых вами потоков.
@Yunnosch в моем коде есть пример возможного массива, массива cells. Если вы посмотрите на код, вы увидите, что его значения первой половины равны 19, а вторая половина - 1 (также описано в OP). есть операторы printf, которые печатают новые значения некоторых ячеек, но вы не можете увидеть много разных значений, потому что программа очень быстро зависает





Вы создали «частные» мьютексы и условные переменные для каждого потока, поэтому они не синхронизируются каким-либо (значимым) способом. Вместо этого:
pthread_mutex_t mutex = globalMutex;
pthread_cond_t condition = globalCond;
Просто используйте globalMutex и globalCond - это то, что вам действительно нужно.
[ Я переместил это сюда, потому что думаю, что мы должны это сделать. Я не могу интуитивно понять ТАК-Икетт. ]
By the way, just to make sure I understand this, the mutex is per cell, so that multiple threads can work on multiple cells simultaneously, right? Just not two threads on the same cell. –
Итак, то, что вы, вероятно, хотите, это что-то вроде:
typedef struct myStruct {
int cellId;
pthread_mutex_t lock;
pthread_cond_t wait;
} myStruct;
и в InitMyStruct ():
myStructs[i]->cellId = i % THREADS_NUM;
pthread_mutex_init(&myStructs[i]->lock, NULL);
pthread_cond_init(&myStructs[i]->wait, NULL);
и в Halvers:
pthread_mutex_lock(&myStr->lock);
cells[id] /= 2;
pthread_cond_broadcast(&myStr->wait);
pthread_mutex_unlock(&myStr->lock);
и дублер: ...
pthread_mutex_lock(&myStr->lock);
while((cells[id] * 2) > MAX) {
printf("Waiting... id = %d\n", id);
pthread_cond_wait(&myStr->wait, &myStr->lock);
}
cells[id] *= 2;
printf("new val = %d, id = %d\n", cells[id], id);
pthread_mutex_unlock(&myStr->lock);
So currently, only one thread can make changes to the array at a time? But then the program exits after about a second, if threads couldn't be making changes to the array simultaneously then wouldn't the program take 10 seconds to finish, because each HalverThread sleeps for 1 second. – Yos 6 hours
Халверсы спят, прежде чем схватить мьютекс, поэтому все спят почти одновременно, просыпаются, борются за мьютекс и продолжают.
Потрясающе, спасибо! Я был смущен тем, что локальная переменная pthread_mutex_t «указывает» на глобальную (потому что это структура), но теперь я понимаю, что для этого мне пришлось бы использовать явный указатель. Смущает Ява.
«Ява смущает» - разве мы не все :)
Кстати, чтобы убедиться, что я это понимаю, мьютекс для каждой ячейки, так что несколько потоков могут работать с несколькими ячейками одновременно, верно? Только не два потока в одной ячейке.
Для этого вам понадобится либо массив из мьютексов + condvar, либо какой-либо другой способ связать их с ячейкой. Важно то, что единственный определенный способ гарантировать, что оба потока используют один и тот же мьютекс, - это использовать один и тот же адрес для структуры мьютекса. Копии могут работать в некоторых реализациях, но не во всех.
Итак, в настоящее время только один поток может вносить изменения в массив за раз? Но затем программа завершается примерно через секунду, если потоки не могут одновременно вносить изменения в массив, тогда программа не займет 10 секунд, потому что каждый HalverThread спит 1 секунду.
Можете ли вы показать пример, предоставив массиву init, а затем многократно показав содержимое массива с течением времени? Вы также можете немного объяснить, почему вы этого хотите, это может помочь понять ваши побочные требования и тем самым упростить вам помощь.