【问题标题】:Is there an invalid pthread_t id?是否存在无效的 pthread_t id?
【发布时间】:2021-11-30 16:01:13
【问题描述】:

我想为给定的线程 ID 调用 pthread_join,但前提是该线程已启动。安全的解决方案可能是添加一个变量来跟踪哪个线程在哪里启动。但是,我想知道是否可以检查 pthread_t 变量,类似于以下代码。

pthread_t thr1 = some_invalid_value; //0 ?
pthread_t thr2 = some_invalid_value;

/* thread 1 and 2 are strated or not depending on various condition */
....

/* cleanup */
if(thr1 != some_invalid_value)
    pthread_join(&thr1);

if(thr2 != some_invalid_value)
    pthread_join(&thr2);

其中 some_invalid_value 可以是 0,或依赖于实现的“PTHREAD_INVALID_ID”宏

PS: 我的假设是 pthread_t 类型是可比较和可分配的,假设基于

PPS: 我想这样做,因为我认为在无效线程 id 上调用 pthread_join 是未定义的行为。它不是。但是,加入先前加入的线程是未定义的行为。现在让我们假设上面的“函数”被重复调用。 无条件调用 pthread_join 并检查结果可能会导致在先前加入的线程上调用 pthread_join。

【问题讨论】:

  • 您总是可以使用 pthread_self()... 一个您可能不会加入的线程 ;-)。可以使用pthread_t invalid_thread = pthread_self() 来提高可读性。

标签: linux pthreads


【解决方案1】:

你的假设一开始就不正确。 pthread_t 对象是不透明的。您不能直接在 C 中比较 pthread_t 类型。您应该改用 pthread_equal。

另一个考虑是如果 pthread_create 失败,你的 pthread_t 的内容将是未定义的。它可能不再设置为您的无效值。

我的偏好是保留 pthread_create 调用的返回值(连同线程 ID),并使用它来确定每个线程是否正确启动。

【讨论】:

  • 可以直接在 C 中比较 pthread_t 类型。sys/types.h 声明所有类型都定义为算术类型(除了一些不包括 pthread_t 的例外)。我认为您的意思是您不应该比较 pthread_t 类型
  • @OLL: pthread 并不总是算术类型。对于 unix/Linux 是正确的,但对于其他操作系统可能是错误的,例如 Windows (Win32)
【解决方案2】:

按照 Tony 的建议,您可以在这种情况下使用 pthread_self()

不要使用==!= 比较thread_ts。使用pthread_equal

来自pthread_self 手册页:

因此,pthread_t 类型的变量无法使用 C 等式运算符 (==) 进行可移植比较;请改用 pthread_equal(3)。

【讨论】:

  • +1 - 只是根据 qbert220 的建议来详细说明使用单独的标志来跟踪无效线程,这里的用法是:if (pthread_create(&thr, ...) != 0) thr = pthread_self();
【解决方案3】:

我最近遇到了同样的问题。如果 pthread_create() 失败,我最终会在我的 phtread_t 结构中存储一个未定义的无效值。因此,如果 pthread_create() 成功,我会保留一个与每个线程关联的布尔值。

那么我需要做的就是:

void* status;
if (my_thread_running) {
  pthread_join(thread, &status);
  my_thread_running = false;
}

【讨论】:

    【解决方案4】:

    不幸的是,在pthread_t 是指针的系统上,pthread_equal() 可以返回相等,即使两个 args 引用不同的线程,例如一个线程可以退出并可以使用相同的pthread_t 指针值创建一个新线程。

    【讨论】:

    • 在这种情况下,直接比较当然也会失败。 :-)
    【解决方案5】:

    这是一个很好的问题,我真的希望在 C++ 类和代码测试中得到更多讨论。

    某些系统的一个选项——这对你来说可能看起来有点过头了,但对我来说已经派上了用场——是启动一个线程,它除了有效地等待一个拆卸信号,然后退出之外什么都不做。该线程在应用程序的整个生命周期内保持运行,在关闭序列中很晚才停止。在那之前,这个线程的 ID 可以有效地用作“无效线程”值——或者,更有可能的是,作为“未初始化”的哨兵——用于大多数用途。例如,我的调试库通常会跟踪锁定互斥锁的线程。这需要将该跟踪值初始化为合理的值。因为 POSIX 相当愚蠢地拒绝要求平台定义 INVALID_THREAD_ID,并且因为我的库允许 main() 锁定事物(使 pthread_self 检查是一个很好的解决方案 pthread_create 无法用于锁定跟踪),所以这是我开始使用的解决方案.它适用于任何平台。

    但是请注意,如果您希望它能够将静态线程引用初始化为无效值,您还需要做更多的设计工作。

    【讨论】:

      【解决方案6】:

      我正在将一些使用 pthread 的代码移植到 C++ 应用程序中,我也有同样的问题。我决定切换到 C++ std::thread 对象更容易,它有 .joinable() 方法来决定是否加入,即

       if (t.joinable()) t.join();
      

      我发现只是在错误的 pthread_t 值上调用 pthead_join(由于 pthread_create 失败)会导致段错误,而不仅仅是错误返回值。

      【讨论】:

        【解决方案7】:

        我们的问题是我们无法知道 pthread 是否已启动,因此我们将其设为指针并分配/取消分配它,并在不使用时将其设置为 NULL:

        开始:

        pthread_t *pth = NULL;
        
        pth = malloc(sizeof(pthread_t));
        int ret = pthread_create(pth, NULL, mythread, NULL);
        if( ret != 0 )
        {
            free(pth);
            pth = NULL;
        }
            
        

        然后当我们需要加入时,无论线程是否启动:

        if (pth != NULL)
        {
            pthread_join(*pth, NULL);
            free(pth);
            pth = NULL;
        }
        

        如果您需要快速重生线程,那么 malloc/free 循环是不可取的,但它适用于像我们这样的简单情况。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-12
          • 1970-01-01
          • 2020-05-14
          • 1970-01-01
          • 1970-01-01
          • 2020-11-11
          相关资源
          最近更新 更多