c - pthread_cond_timedwait() 如何工作?

标签 c multithreading pthreads mutex thread-synchronization

所以我试图了解 pthread_cond_timedwait() 是如何工作的,因为我在项目上进行同步时遇到了一些问题。这是我想出的代码,但它并没有像我想象的那样工作。我的目标是打印时间,等待 2 秒,然后再次打印时间以查看时间的流逝。

//gcc -Wall -pthread timedwait.c -o  exe


#define _OPEN_THREADS                                                           
#include <pthread.h>                                                            
#include <stdio.h>                                                              
#include <time.h>                                                               
#include <errno.h>
#include <stdlib.h>




int main() {                                                                        
  pthread_cond_t cond;                                                          
  pthread_mutex_t mutex;                                                        
  time_t T;                                                                     
  struct timespec t;                                                            

  if (pthread_cond_init(&cond, NULL) != 0) {                                    
    perror("pthread_cond_init() error");                                        
    exit(2);                                                                    
  }                                                                             


  time(&T);                                                                     
  t.tv_sec = T + 2;                                                             
  printf("starting timedwait at %s", ctime(&T));                                
  pthread_cond_timedwait(&cond, &mutex, &t);                                                                                               
  time(&T);                                                                     
  printf("timedwait over at %s", ctime(&T));                                  
}

最佳答案

您的代码存在几个问题,最重要的问题都与使用值不确定的变量有关。

您的其他答案讨论了您未能初始化结构t的某些成员的事实,这确实是一个重大缺陷。当我修改您的程序以捕获 pthread_cond_timedwait 调用的返回值时,我发现它返回 EINVAL,表明参数无效。有两个很好的选择可以解决该问题:

  • 使用初始值设定项声明该变量:

    struct timespec t = { 0 };
    // ...
    t.tv_sec = T + 2;
    

    该特定的初始化程序将第一个成员显式设置为零,所有其他成员都隐式设置为默认(零)值 - 这是不适用于逐个成员分配的初始化特征。这不仅比显式分配每个成员更方便,而且还可以处理该结构类型的特定实现版本可能具有的任何未记录的成员。

  • 或者通过达到目的的函数为(整个)结构设置一个值。例如,

    struct timespec t;
    clock_gettime(CLOCK_REALTIME, &t);
    // ... no need for T or time() ...
    t.tv_sec += 2;
    

    这需要一个不依赖于结构原始值的函数(例如clock_gettime)。

纠正变量t的初始化问题后,生成的程序对我来说仍然失败,但出现不同的错误:EPERM,表明不允许该操作参数的特定组合。对于这个特定的函数,这是因为互斥体在调用时没有被调用线程锁定,但实际上比这更糟糕:互斥体甚至没有初始化。可以通过 pthread_mutex_init 来初始化它,但如果您不想设置任何非默认属性,那么静态初始化器会更方便:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int result;
// ...
result = pthread_mutex_lock(&mutex);

(注意:条件变量也有一个静态初始值设定项。)

此外,正如我在评论中观察到的那样,您并没有始终如一地检查函数调用的返回值。如果您不关心调用的成功或效果,或者至少可以合理地忽略可能发生的任何失败,但您的程序的后续行为依赖于特定结果,则可以忽略此类检查正确地说,检查返回值是安全编程的一个基本问题。

最后,作为一个小问题,GCC 和 GLibc 认为 _OPEN_THREADS 功能测试宏没有任何意义,并且它不是由 POSIX 定义的。它似乎特定于 IBM 的工具链。对于您的情况来说,它可能没有害处,但它绝对不合适并且没有帮助,即使在它应用的环境中,似乎也有更好的方法来获得它所提供的相同效果。

您的程序的这一变体解决了所有这些问题:

// gcc -Wall -pthread timedwait.c -o  exe

#include <pthread.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

int main() {
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    struct timespec t;
    int result;

    result = pthread_mutex_lock(&mutex);
    if (result != 0) {
        fprintf(stderr, "pthread_mutex_lock: %s\n", strerror(result));
        exit(EXIT_FAILURE);
    }

    result = clock_gettime(CLOCK_REALTIME, &t);
    if (result == -1) {
        perror("clock_gettime");
        exit(EXIT_FAILURE);
    }

    // Return-value check is non-essential here:
    printf("starting timedwait at %s", ctime(&t.tv_sec));

    t.tv_sec += 2;
    result = pthread_cond_timedwait(&cond, &mutex, &t);
    if (result != ETIMEDOUT) {
        fprintf(stderr, "%s\n", strerror(result));
    }

    result = clock_gettime(CLOCK_REALTIME, &t);
    if (result == -1) {
        perror("clock_gettime");
        exit(EXIT_FAILURE);
    }

    // Return-value check is non-essential here:
    printf("timedwait over at %s", ctime(&t.tv_sec));

    // Return-value check is non-essential here, because we'll just exit anyway:
    pthread_mutex_unlock(&mutex);
}

最后,我发现在我自己的编程中,我通常会定义一个或多个宏来支持返回值检查。我发现使用这样的宏而不是为每个检查显式地编写代码可以使程序的整体流程更加清晰,更不用说节省我的击键次数了。我没有在上面证明这一点,尽管即使在如此短的程序中,这种方法的有用性可能已经很明显。

关于c - pthread_cond_timedwait() 如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59112776/

相关文章:

c - 为什么逗号运算符似乎不能在我的代码中的 "if"语句和 "else"语句之间工作?

c - 使用 malloc 恢复.c

c - dlopen:检测具有循环依赖性的 undefined symbol

c++ - tcp通信的线程

c - realloc() 有任何限制吗

c# - 为什么暂停/中止线程不好?

c# - SemaphoreSlim 的超时是否违背了它自己的目的?

c - C中的多编写器线程安全队列

c++ - mem_fun 失败,pthread 和类 ptr

c - C 线程间传递消息