“pthread_join”不会在刚刚取消的线程上返回使用“pthread_cancel”

问题描述

我的程序中有一个线程池(QueueWorkers 类),这些线程使用此逻辑释放:

int QueueWorkers::stop()
{
  for (unsigned int ix = 0; ix < threadIds.size(); ++ix)
  {
    pthread_cancel(threadIds[ix]);
    pthread_join(threadIds[ix],NULL);
  }

  return 0;
}

其中 threadIds 是类型为 std::vector<pthread_t>类变量

这个逻辑在大多数情况下都有效,但我已经检查过它有一定概率失败的测试。特别是,有时在执行 pthread_cancel 后,下一行中的 pthread_join 语句永远不会返回并且我的程序挂起。

据我目前所知,在取消的线程上使用 pthread_join 应该总是返回。是否有任何情况可以避免这种情况或以任何方式调试这里发生的事情?我在终止时释放线程的方法是否正确?

附加信息:线程有一个取消处理程序(使用 pthread_cleanup_push 注册),它释放线程使用的动态内存以避免泄漏。在正常情况下,处理程序在 pthread_cancel 上被调用并且工作正常,但是时间 pthread_join 失败返回我检查过取消处理程序没有被调用

提前致谢!

编辑: 正如问题评论中所建议的,我修改了我的代码以检查 pthread_cancel 的返回值。无论之后 pthread_join 是否按预期工作,它始终为 0。

EDIT2:根据对此问题的一些评论的要求,让我提供有关其工作原理的更多详细信息。

线程池由start()方法初始化:

int QueueWorkers::start()
{
  // numberOfThreads and pQueue are class variables
  for (int i = 0; i < numberOfThreads; ++i)
  {
    pthread_t  tid;
    pthread_create(&tid,NULL,workerFunc,pQueue);  
    threadIds.push_back(tid);
  }

  return 0;
}

启动函数workerFunc()如下(简化):

static void* workerFunc(void* pQueue)
{
  // Initialize some dynamic objects (Foo for simplification)
  Foo* foo = initFoo();

  // Set pthread_cancel handler
  pthread_cleanup_push(workerFinishes,foo);

  // Loop forever
  for (;;)
  {
    // Wait for new item to process on pQueue
    ... paramsV = ((Queue*) pQueue)->pop();

    // Then process it
    ...
  }

  // Next statemement never executes but compilation breaks without it. See this note in pthread.h:
  // "pthread_cleanup_push and pthread_cleanup_pop are macros and must always be used in
  // matching pairs at the same nesting level of braces".
  pthread_cleanup_pop(0);
}

在开始以太循环之前注意 pthread_cleanup_push() 语句。这样做是为了在取消 Foo 对象时实现清理逻辑:

static void workerFinishes(void* curl)
{
  freeFoo((Foo*) curl);
}

我希望没有过度简化代码。在任何情况下,您都可以看到原始版本 here

解决方法

确定线程处于取消状态还是您的线程 cancelation_type 是异步的?

来自 manpthread_cancel

一个线程的取消类型,由 pthread_setcanceltype(3),可以是异步的或延迟的( 新线程的默认值)。异步可取消性意味着 线程可以 随时取消(通常是立即取消,但系统不保证这一点)。延迟可取消性意味着取消 将被延迟,直到线程下一次调用一个函数 取消点。 pthreads(7) 中提供了是或可能是取消点的函数列表。

我不认为取消线程是确保线程完成的最佳方法。也许您可以向线程发送一条消息,让它停止并确保线程确实收到消息并会处理它。