pthread不会在C中退出

时间:2017-05-10 23:39:18

标签: c multithreading pthreads

我正在编写一个多线程应用程序,它读取一个文件,并在内存中寻找一个线程块中的单词。

如果首先找到该字符,则线程需要异步关闭寻找该字的其他线程。

问题是当找到一个单词并且其他线程被关闭时,程序不会终止(在10次执行中有6次)。我已经检查了gdb,一个线程没有退出。即使我没有拨打waitforthreads(n_threads),也会发生这种情况。

// [...]
FILE* f;
pthread_mutex_t mutex;
pthread_t* threads;
int n_threads;
int allread;
// [...]

int main(int argc, char* argv[]) {
  // [...]
  threads = (pthread_t*) calloc(n_threads, sizeof(pthread_t));
  pthread_mutex_init(&mutex, NULL);

  runthreads(f, word, n_threads, n_records);
  waitforthreads(n_threads);

  pthread_mutex_destroy(&mutex);
  // [...]
}


void runthreads(FILE* f, char* w, int n_threads, int n_records) {
  struct targs_t args = {w, n_records};
  for (int i=0; i<n_threads; i++)
    pthread_create(&threads[i], NULL, findword, (void*) &args);
}


void waitforthreads(int N) {
  for (int i=0; i<N; i++)
    if(pthread_join(threads[i], NULL))
      exit_(6);
}


void* findword(void* arg) {
  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

  struct targs_t* args = (struct targs_t*) arg;
  int max_length = args->n_records * sizeof(record_t);
  record_t* records = malloc(max_length);

  int found = 0;
  while (!allread && !found) {
    pthread_mutex_lock(&mutex);
    // allread is being set in the function below 
    // if the whole file has been read
    readRecords((char*) records, args->n_records, f);
    pthread_mutex_unlock(&mutex);

    for (int i=0; i<args->n_records; i++)
      if (strlen(records[i].text) == 0) break;
      else if (strstr(records[i].text, args->word) != NULL) {
        notifyfound(pthread_self(), records[i].id);
        found = 1;
        break;
      }
  }
  free(records);
  return NULL;
}


void notifyfound(pthread_t tid, int id) {
  printf("Found: %d (%ld)\n", id, (long) tid);
  for (int i=0; i<n_threads; i++)
    if (threads[i] && !pthread_equal(threads[i], tid)) {
      printf(" closing %ld\n", (long) threads[i]);
      pthread_cancel(threads[i]);
    }
  printf(" finished closing\n");
}

1 个答案:

答案 0 :(得分:1)

这与取消点有关,虽然具体细节很难得到,因为你还没有分享一个最小的例子。我的诊断是

  1. 6/10次您至少有一个线程正在等待互斥锁,而另一个线程在readRecords中,这将取消并且不释放互斥锁。设置取消处理程序pthread_cleanup_pushpthread_cleanup_pop将释放您的互斥锁,并阅读pthread_cancel的手册。有关参考资料,请参阅相关的pthread_cleanup_push causes Syntax error

  2. 您的某些主题未检测到取消 - 尝试使用pthread_testcancel设置有保证的取消点。

  3. 以下是一些通过添加取消检查和互斥清理修复这些问题的代码。

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    FILE* f;
    pthread_mutex_t mutex;
    pthread_t* threads;
    int n_threads = 3;
    int allread;
    long int count = 0;
    int *thread_ids;
    int global_quit = 0;
    #define MAX 99999
    
    void waitforthreads(int N) {
      printf("waiting for %d threads\n", N);
      for (int i=0; i<N; i++)
        {
          printf("thread %d | %d\n", i, threads[i]);
          if(pthread_join(threads[i], NULL))
            {
              printf("problem\n");
              exit(6);
            }
        }
      printf("done.\n");
    }
    
    void notifyfound(pthread_t tid, int count) {
      printf("%d | %d got big number\n", count, pthread_self());
      for (int i=0; i<n_threads; i++)
        if (threads[i] && !pthread_equal(threads[i], tid)) {
          printf(" closing '%ld'\n", (long) threads[i]);
          pthread_cancel(threads[i]);
        }
      global_quit = 1;
      printf(" finished closing\n");
    }
    
    void waiting_thread_cleanup(void *arg)
    {
      pthread_mutex_unlock((pthread_mutex_t *)arg);
    }
    
    void* do_thing(void* arg) {
      pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
      pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    
      int* id = (int *)arg;
      int quit = 0;
      while (!allread) {
        pthread_mutex_lock(&mutex);
        pthread_cleanup_push(waiting_thread_cleanup, (void *)&mutex); /* must be paired with pop. */
        if(count++==MAX)
          {
            notifyfound(pthread_self(), *id);
            quit=1;
          }
        else if(count % 10000 == 0)
          printf("[%d] - %d\n", *id, count);
        pthread_testcancel();       /* required to allow for the cancel to ever be 'detected' other functions are sufficient as well. */
        pthread_mutex_unlock(&mutex);
        pthread_cleanup_pop(1);     /* if this isn't here, this will occassionally hand because the mutex isn't freed. */
        if(quit==1)
          {
            printf("%d | %d quitting\n", *id, pthread_self());
            break;
          }
      }
      return NULL;
    }
    
    void runthreads(FILE* f, int n_threads) {
      for (int i=0; i<n_threads; i++)
        pthread_create(&threads[i], NULL, do_thing, &(thread_ids[i]));
    }
    
    int main(int argc, char* argv[]) {
      threads = (pthread_t*) calloc(n_threads, sizeof(pthread_t));
      thread_ids = (int*) calloc(n_threads, sizeof(int));
      for(int i=0;i<n_threads;i++)
        thread_ids[i] = i;
    
      pthread_mutex_init(&mutex, NULL);
    
      runthreads(f, n_threads);
      waitforthreads(n_threads);
    
      pthread_mutex_destroy(&mutex);
    }