gpt4 book ai didi

c - pthread_cond_wait 无限等待的可能原因

转载 作者:太空宇宙 更新时间:2023-11-03 23:28:46 27 4
gpt4 key购买 nike

我目前正在尝试分析第三方源代码中的一个问题,其中线程(对应于 THREAD-T1 的代码片段)处于无限等待状态。怀疑是线程卡在了pthread_cond_wait。以下是相同的详细信息。

代码说明

  • T1 对 T2 公开的 API 进行异步调用。
  • 因此,T1 进入条件变量(例如 cond_t)的阻塞等待。
  • 条件变量 cond_t 在 T2 生成的回调事件中发出信号。
  • 上述循环重复 n 次,直到 API 返回成功。

综合起来,以上是一系列的步骤,通过使用条件变量使异步调用类似于同步调用。

示例代码

   #define MAX_RETRY (3)
bool g_b_ret_val;
pthread_mutex_t g_cond_mutex;
pthread_mutex_t g_ret_val_mutex; /* Assume iniitailzed in the main thread */
pthread_cond_t g_cond_t; /* Assume iniitailzed in the main thread */

retry_async_call_routine() /* Thread-T1 */
{
while(( false == g_b_ret_val) && (retry < MAX_RETRY))
{
(void)invoke_async_api();
pthread_mutex_init(&g_cond_mutex, NULL);
pthread_mutex_lock(&g_cond_mutex);
pthread_cond_wait(g_cond_t, &g_cond_mutex);
pthread_mutex_unlock(&g_cond_mutex);
pthread_mutex_destroy(&g_cond_mutex);
retry ++ ;
}
}

callback_routine() /* Thread-T2 */
{
pthread_mutex_lock(&g_ret_val_mutex);
g_b_ret_val = true; /* May be false also on failure */
pthread_mutex_unlock(&g_ret_val_mutex);
pthread_cond_signal(&g_cond_t);
}

我在代码中看到的已知问题

  1. pthread_cond_wait 的 while 循环中缺少对条件的重新测试
  2. 发信号时缺少互斥锁

问题

  1. 请指出任何更多的循环漏洞(或)无限等待的可能性(如果有的话)。
  2. g_cond_t 未在连续等待之间使用 pthread_cond_destroy 重置,相同的行为是什么? (有关此的任何引用)

最佳答案

这段代码看起来很荒谬。您不应该仅仅为了等待条件变量而创建和销毁互斥锁​​。在使用线程共享数据之前需要创建一个互斥量,然后必须使用互斥量来保护共享数据。在这种情况下,这是保护 g_b_ret_valg_ret_val_mutex

条件变量本身仅用于等待(定期或定时等待)和信号(信号或广播)。它通常不需要自己的锁,事实上,有一个单独的锁(如在上面的循环中)会妨碍调用 pthread_cond_wait,它只需要 一个要解锁的互斥量,而不是两个。除非您需要新的/不同的属性,否则无需销毁和重新创建条件变量。

“不卡住”(避免无限等待)的关键是保证,每当一个线程调用 pthread_cond_wait 时,肯定会有其他线程在未来调用 pthread_cond_signal(或 pthread_cond_broadcast)。也就是说,服务员首先测试“为什么要等待”,锁定“为什么”部分,然后仅当“为什么”部分说“你应该等待”时才等待。唤醒线程可以使用相同锁来确定唤醒是必要的,或者——如果唤醒线程是“懒惰的”,如上例所示——简单地发出一个“每次叫醒电话。

因此,正确性的最小更改似乎是将循环更改为:

pthread_mutex_lock(&g_ret_val_mutex);
for (retry = 0; retry < MAX_RETRY && !g_b_ret_val; retry++) {
(void)invoke_async_api();
pthread_cond_wait(&g_cond_t, &g_ret_val_mutex);
}
success = g_b_ret_val; /* if false, we failed */
/* or: success = retry < MAX_RETRY; -- same result */
pthread_mutex_unlock(&g_ret_val_mutex);

(另外:g_cond_t 是变量的糟糕名称;_t 后缀用于类型。)


有时将“某个线程需要唤醒”与“该线程的最终结果是成功”分开是明智的。如果需要,我可能会使用第二个 bool 值添加它。我们称它为 g_waiting,当 callback_routine()(假设)保证被调用并且它应该进行唤醒时,我们将其设置为 true事件,当不能保证被调用或不需要唤醒时为false。这种编码允许您切换到 pthread_cond_timedwait,以防异步事件由于某种原因可能永远不会发生。

考虑到 g_ret_val_mutex 保护 g_b_ret_val,将它用于“等待”标志也是合适的——添加另一个互斥量只会提供更多出现问题的机会,在这里。所以现在我们得到:

pthread_mutex_lock(&g_ret_val_mutex);
for (retry = 0; retry < MAX_RETRY && !g_b_ret_val; retry++) {
(void)invoke_async_api();
compute_wakeup_time(&abstime);
g_waiting = true;
pthread_cond_timedwait(&g_cond_t, &g_ret_val_mutex, &abstime);
if (g_waiting) {
/* timeout occurred, we never got our callback */
/* may want something special for this case */
} else {
/* wakeup occurred, result is in g_b_ret_val */
}
}
success = g_b_ret_val;
/* or: success = retry < MAX_RETRY; */
g_waiting = false;
pthread_mutex_unlock(&g_ret_val_mutex);

同时:

callback_routine() /* Thread-T2 */
{
pthread_mutex_lock(&g_ret_val_mutex);
g_b_ret_val = compute_success_or_failure();
if (g_waiting) {
g_waiting = false;
pthread_cond_signal(&g_cond_t);
}
pthread_mutex_unlock(&g_ret_val_mutex);
}

我已将 signal 移动到互斥锁“内部”,尽管无论哪种方式都可以,因此只有在设置了 g_waiting 并清除时我才能这样做等待。由于我们持有互斥量,因此可以在调用 pthread_cond_signal 之前或之后清除 g_waiting(只要没有其他代码会中断该序列)。

注意:如果我们确实开始使用 timedwait,我们需要确定是否可以在另一个较早的 invoke 调用时调用 invoke_async_api使用但在超时前没有返回结果。

关于c - pthread_cond_wait 无限等待的可能原因,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20385126/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com