Как присоединиться к какой-либо теме заканчивается первым?
Основной поток порождает> 1 потока, и каждый из них может вызвать return со значением ошибки. Если это происходит, другие потоки не имеют смысла продолжать работу, поэтому их следует отменить.
Итак, я хочу, чтобы моя основная тема:
- Присоединяйтесь к тому, какой поток завершит первым;
- Проверьте, вернул ли этот поток ошибку, и если да, отмените все остальные потоки
Тем не мение, pthread_join
требует от меня указать, к какой теме я хочу присоединиться. Если я позвоню, например, pthread_join(thread1, thread1_ret)
, а также thread2
заканчивается с ошибкой, тогда я не смогу знать, что thread2
закончил по ошибке раньше thread1
заканчивается, и тот факт, что thread2
преждевременное завершение вполне может означать, что thread1
в настоящее время ожидает условную переменную, которая никогда не будет сигнализироваться, потому что только thread2
может сигнализировать эту переменную... Итак, плохо.
Я хочу, чтобы мой основной поток отменить thread1
если thread2
заканчивается и наоборот.
Как это сделать?
2 ответа
Как это сделать?
Вам нужен отдельный канал связи.
Типичное решение включает в себя очередь (из законченных потоков) и условную переменную.
Поток, завершающийся с ошибкой, помещает себя в очередь и сигнализирует об условии перед возвратом. Основной поток ожидает выполнения условия, проверяет очередь и присоединяется к потоку, который там находится, а затем отменяет все остальные потоки.
Также обратите внимание, что отмена асинхронного потока является сложной задачей. Обычно лучше иметь глобальную переменную, которую все потоки периодически проверяют: while (!exit_requested) { do_work(); }
Вы хотите что-то вроде этого:
struct thread_data {
int stop;
pthread_cond_t * flag;
pthread_mutex_t * lock;
int * failCount;
int * successCount;
};
void * foobar(void * ptr)
{
struct thread_data * data = (struct thread_data*)ptr;
int fail = 0;
while (isWorkToBeDone() && !data->stop) {
// do some work
if (encounteredError()) {
pthread_mutex_lock(data->lock);
data->failCount += 1;
fail = 1;
pthread_cond_signal(data->flag);
pthread_mutex_unlock(data->lock);
}
}
// clean up
if (!fail) {
pthread_mutex_lock(data->lock);
data->successCount += 1;
pthread_cond_signal(data->flag);
pthread_mutex_unlock(data->lock);
}
pthread_exit(NULL);
}
int runThreads()
{
pthread_t * threads;
pthread_mutex_t lock;
pthread_cond_t flag;
int i;
struct thread_data data;
threads = malloc(sizeof(*threads)*numThreads);
if (!threads) {
// handle malloc error
return 0;
}
// initialize mutex and condition variable
pthread_mutex_init(&lock, NULL);
pthread_cond_init(&flag, NULL);
data.stop = 0;
data.flag = &flag;
data.lock = &lock;
data.failCount = 0;
data.successCount = 0;
for (i = 0; i < numThreads; ++i) {
if (!pthread_create(threads+i, NULL, foobar,
(void *)(threads+i))) {
// handle thread creation error
}
}
while (!data.stop) {
pthread_mutex_lock(&lock);
pthread_cond_wait(&cond, &lock);
// a thread just finished
if (data.failCount > 0) {
data.stop = 1;
} else if (data.successCount == numThreads) {
data.stop = 1;
}
pthread_mutex_unlock(&lock);
}
for (i = 0; i < numThreads; ++i) {
pthread_join(threads[i], NULL);
}
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&flag);
free(threads);
}
Это работает так: вы ждете, когда один из потоков выйдет из строя, или все потоки будут выполнены успешно, а затем дадите знать всем потокам, что они перестали работать и завершите работу через stop
переменная.
Вместо того, чтобы темы проверить stop
переменная, вы могли бы назвать pthread_kill()
, но гораздо лучше выходить из каждого потока чисто.