Как присоединиться к какой-либо теме заканчивается первым?

Основной поток порождает> 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(), но гораздо лучше выходить из каждого потока чисто.

Другие вопросы по тегам