Мне нужен код C для программы, в которой N потоков могут взаимодействовать друг с другом.
каждый идентификатор потока может ждать любой идентификатор потока, который ему нужен.
каждый идентификатор потока может сигнализировать, какой идентификатор потока ему нужен.
они могут ждать и сигнализировать друг другу несколько раз.
когда поток получает сигнал, это означает, что он может перестать ждать потока и что два потока синхронизированы, как барьер, но вместо этого реализованы с помощью мьютекса и условных переменных.
каждый поток, который ожидает, ожидает сигнала от определенного потока с идентификатором, называемым приятелем, и с правильными int:synch_point и int:group. Если они не совпадают, поток должен продолжать ждать, пока такой сигнал не будет получен.
void signal_and_wait_for_thread(thread_data_t *thread_data, int myid, int buddy, int sync_point, int stride) {
// Lock myid mutex and update the shared data
pthread_mutex_lock(&thread_data->mutexes[myid]);
thread_data->sync_points[myid] = sync_point;
thread_data->strides[myid] = stride;
//thread_data->ready[myid] = 1;
pthread_cond_broadcast(&thread_data->conds[myid]);
pthread_mutex_unlock(&thread_data->mutexes[myid]);
// Lock buddy mutex and wait for the buddy thread to reach the same sync point and stride
pthread_mutex_lock(&thread_data->mutexes[buddy]);
while (
thread_data->sync_points[buddy] != sync_point ||
thread_data->strides[buddy] != stride) {
pthread_cond_wait(&thread_data->conds[buddy], &thread_data->mutexes[buddy]);
}
//thread_data->ready[buddy] = 0;
pthread_mutex_unlock(&thread_data->mutexes[buddy]);
}
Приведенный выше код пытается реализовать этот сигнал и ждать нескольких идентификаторов потока. Проблема в том, что дело зашло в тупик. Каков правильный способ синхронизации двух потоков среди N потоков, которые могут захотеть взаимодействовать с одним и тем же потоком? без внесения слишком больших накладных расходов с ненужными мьютексами.
Если A ждет B, а B ждет A, никакая реализация не сможет предотвратить взаимоблокировку.
@ScottHunter Но должен быть способ правильно синхронизировать два потока, без использования барьеров, которые необходимо инициализировать для каждого взаимодействия.
AFAICT, барьерам pthread нужен один вызов init и один вызов уничтожения, но может быть много вызовов ожидания
Я не уверен, что выполняю здесь требования. Можно ли в данный произвольный момент времени разделить потоки на непересекающиеся множества тех, кто ожидает сигнала, и тех, кто отправляет сигнал? Или данный поток может одновременно ожидать получения и пытаться отправить? (И если да, то как это должно работать?) Когда вы говорите «поток может сигнализировать», вы имеете в виду pthread_cond_signal()
, или мы могли бы вместо этого сказать «[попытаться] отправить сообщение»?
Не по теме, но... зачем вам (или думаете, что вам нужна) эта функциональность? У вас есть вариант использования? Разработка такой общей системы блокировки/сигнализации, в то же время гарантирующей отсутствие взаимоблокировок, является - по крайней мере для меня - огромной задачей (если я не неправильно прочитал требования).
Код в вашем вопросе выглядит нормально для реализации описанной вами схемы. Если предположить, что все переменные инициализированы правильно, проблема, скорее всего, заключается в том, как вы используете эту функцию, поскольку она весьма склонна к взаимоблокировке, если логика вашей вызывающей программы ошибочна.
Если, например, у вас есть вызов потока A signal_and_wait_for_thread(data, ID_A, ID_B, 1, 10);
и поток B вызывает signal_and_wait_for_thread(data, ID_B, ID_A, 1, 20);
, то это, очевидно, заблокируется: поток A будет ждать, пока поток B установит шаг 10, а поток B будет ждать, пока поток A установит шаг 20, и ни один из них никогда не сможет добиться прогресса.
void wait_for_buddy(thread_data_t * thread_data, int myid, int buddy) {
pthread_mutex_lock( & thread_data -> mutexes[buddy + 1]);
while (!thread_data -> ready[myid][buddy]) {
pthread_cond_wait( & thread_data -> conds[buddy], & thread_data -> mutexes[buddy + 1]);
}
thread_data -> ready[myid][buddy] = 0; // Reset for next sync point
pthread_mutex_unlock( & thread_data -> mutexes[buddy + 1]);
}
void signal_ready(thread_data_t * thread_data, int myid, int buddy) {
pthread_mutex_lock( & thread_data -> mutexes[myid + 1]);
thread_data -> ready[buddy][myid] = 1;
pthread_cond_broadcast( & thread_data -> conds[myid]);
pthread_mutex_unlock( & thread_data -> mutexes[myid + 1]);
}
Просто добавил немного больше объяснений для вас.