Linux多线程编程 - sleep 和 pthread_cond_timedwait

本文探讨了Linux环境下多线程编程中使用sleep与pthread_cond_timedwait进行线程同步的方法。通过对比两种方式,展示了如何利用条件变量实现更精确的线程唤醒机制。

Linux多线程编程 - sleep 和 pthread_cond_timedwait

#include <stdio.h>
#include <stdlib.h>

int flag = 1;
void * thr_fn(void * arg) {
  while (flag){
    printf("******\n");
    sleep(10);
  }
  printf("sleep test thread exit\n");
}
 
int main() {
  pthread_t thread;
  if (0 != pthread_create(&thread, NULL, thr_fn, NULL)) {
    printf("error when create pthread,%d\n", errno);
    return 1;
  }
 
  char c ;
  while ((c = getchar()) != 'q');
 
  printf("Now terminate the thread!\n");
  flag = 0;
  printf("Wait for thread to exit\n");
  pthread_join(thread, NULL);
  printf("Bye\n");
  return 0;
}

输入q后,需要等线程从sleep中醒来(由挂起状态变为运行状态),即最坏情况要等10s,线程才会被join。采用sleep的缺点:不能及时唤醒线程。

 

采用pthread_cond_timedwait函数,条件到了,线程即会被join,可及时唤醒线程。实现的如下:

#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
 
static pthread_t thread;
static pthread_cond_t cond;
static pthread_mutex_t mutex;
static int flag = 1;
 
void * thr_fn(void * arg)
{
  struct timeval now;
  struct timespec outtime;
  pthread_mutex_lock(&mutex);
  while (flag) {
    printf("*****\n");
    gettimeofday(&now, NULL);
    outtime.tv_sec = now.tv_sec + 5;
    outtime.tv_nsec = now.tv_usec * 1000;
    pthread_cond_timedwait(&cond, &mutex, &outtime);
  }
  pthread_mutex_unlock(&mutex);
  printf("cond thread exit\n");
}
 
int main(void)
{
  pthread_mutex_init(&mutex, NULL);
  pthread_cond_init(&cond, NULL);
  if (0 != pthread_create(&thread, NULL, thr_fn, NULL)) {
    printf("error when create pthread,%d\n", errno);
    return 1;
  }
  char c ;
  while ((c = getchar()) != 'q');
  printf("Now terminate the thread!\n");

  pthread_mutex_lock(&mutex);
  flag = 0;
  pthread_cond_signal(&cond);
  pthread_mutex_unlock(&mutex);
  printf("Wait for thread to exit\n");
  pthread_join(thread, NULL);
  printf("Bye\n");
  return 0;
}

pthread_cond_timedwait()函数阻塞住调用该函数的线程,等待由cond指定的条件被触发(pthread_cond_broadcast() or pthread_cond_signal())。

  当pthread_cond_timedwait()被调用时,调用线程必须已经锁住了mutex。函数pthread_cond_timedwait()会对mutex进行【解锁和执行对条件的等待】(原子操作)。

<think>首先,用户的问题是关于条件变量在POSIX线程中的作用以及如何正确使用pthread_cond_timedwait函数。用户引用了多个来源,包括条件变量的基本概念、pthread_cond_waitpthread_cond_timedwait的用法。 我的回答需要遵循系统级指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1]。 用户可见层指令: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 参考站内引用: - [^1]: 条件变量用于线程同步,与互斥量一起使用,避免竞争。 - [^2]: pthread_cond_timedwaitpthread_cond_wait用于同步,支持超时处理。 - [^3]: pthread_cond_timedwait的参数:cond, mutex, abstime(绝对超时时间)。 - [^4]: pthread_cond_wait的步骤:解锁、等待、加锁;使用时需要循环检查以避免信号丢失。 用户需求:了解条件变量在多线程编程中的作用,特别是pthread_cond_timedwait的用法应用场景。 结构化回答: 1. **介绍条件变量的作用**:解释条件变量在POSIX线程中的基本概念,为什么需要它。 2. **解释pthread_cond_timedwait函数**:包括参数、行为、与pthread_cond_wait的区别。 3. **正确使用方式**:如何避免常见错误,如虚假唤醒、信号丢失等。 4. **应用场景**:给出具体例子,如生产者-消费者问题、超时处理等。 5. **生成相关问题**:在末尾添加相关问题。 确保回答真实可靠:基于标准POSIX线程编程知识,引用提供的资料。 数学表达式:用户的问题不涉及数学,但如果有必要,比如在解释算法时,可以使用。但这里可能不需要,除非提到时间计算等。 回答大纲: - **步骤1:介绍条件变量** - 条件变量用于线程同步,允许线程等待特定条件发生。 - 与互斥量配合使用,防止竞争条件。 - 引用[^1]:条件变量给了线程以无竞争的方式等待特定条件发生。 - **步骤2:pthread_cond_timedwait函数详解** - 函数原型:`int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);` - 参数解释:cond(条件变量),mutex(互斥锁),abstime(绝对超时时间)。 - 行为:在等待时解锁互斥量,等待信号或超时后重新加锁。 -pthread_cond_wait的区别:pthread_cond_wait无限期等待,pthread_cond_timedwait有超时机制。 - 引用[^3][^4]:参数步骤。 - **步骤3:正确使用方式** - 使用循环检查条件,避免虚假唤醒(spurious wakeups)。 - 互斥量的加锁解锁顺序。 - 例子:在生产者-消费者模型中。 - 引用[^4]:需要循环检查以避免信号丢失。 - **步骤4:应用场景** - 超时处理:如等待资源时设置超时。 - 实际案例:如线程池、任务调度。 - 引用[^2]:用于代替不可控的sleep函数。 - **步骤5:总结** - **生成相关问题** 在回答中自然地添加引用标识。 最终,生成相关问题: 1. 如何避免条件变量中的虚假唤醒? 2. pthread_cond_timedwait在实时系统中的应用? 3. 条件变量与信号量的区别是什么? 确保语言流畅、中文。</think>### 条件变量在POSIX线程中的作用及pthread_cond_timedwait的正确使用 条件变量是POSIX线程(Pthreads)中用于线程同步的核心机制,它允许线程以无竞争的方式等待特定条件成立(如共享数据状态变化),从而避免忙等待(busy-waiting)造成的资源浪费。条件变量必须与互斥量(mutex)结合使用,互斥量确保对共享资源的原子访问,而条件变量则管理线程的等待唤醒。以下是逐步解释: #### 1. **条件变量的基本作用** - **避免竞争优化性能**:当线程需要等待某个条件(如队列非空)时,直接循环检查会消耗CPU资源。条件变量通过阻塞线程并允许其在条件成立时被唤醒,实现了高效同步。例如,在生产者-消费者模型中,消费者线程可等待生产者通知“数据可用”。 - **与互斥量的协作**:条件变量本身不保护数据,而是依赖互斥量。线程在检查条件前必须先加锁互斥量;若条件不成立,则调用等待函数(如`pthread_cond_wait`)解锁互斥量并阻塞线程;当其他线程改变条件并发送信号时,原线程被唤醒并重新加锁互斥量[^1]。 - **关键优势**:减少线程切换开销,支持多个线程同时等待同一条件,适用于高并发场景如线程池或任务调度。 #### 2. **pthread_cond_timedwait函数详解** `pthread_cond_timedwait`是条件变量等待函数的一种,它在等待条件成立时支持超时机制,适用于需要时间限制的场景(如避免无限期阻塞)。函数原型为: ```c int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); ``` - **参数说明**: - `cond`:指向条件变量的指针。 - `mutex`:指向关联互斥量的指针,调用前必须已被当前线程加锁。 - `abstime`:绝对超时时间,指定为从Epoch(1970-01-01 00:00:00 UTC)开始的秒纳秒结构体(`struct timespec`)。例如,等待5秒:`abstime = { .tv_sec = time(NULL) + 5, .tv_nsec = 0 }`[^3]。 - **函数行为**: - 调用时,线程自动解锁`mutex`并进入阻塞状态,等待条件变量被触发或超时。 - 当条件被触发(如其他线程调用`pthread_cond_signal`)或超时到达时,线程被唤醒并尝试重新加锁`mutex`。 - 返回值: - 成功时返回0(条件成立)。 - 超时返回`ETIMEDOUT`。 - 错误返回非零值(如`EINVAL`参数无效)。 - **与pthread_cond_wait的区别**:`pthread_cond_wait`无限期等待,而`pthread_cond_timedwait`引入超时机制,适用于需超时处理的场景(如网络请求响应或资源等待)[^2]。 #### 3. **正确使用pthread_cond_timedwait的步骤** 为避免竞争、虚假唤醒(spurious wakeups)或信号丢失,必须遵循以下模式: - **步骤1: 加锁互斥量**:在检查条件前,先加锁关联互斥量。 - **步骤2: 循环检查条件**:使用`while`循环而非`if`检查条件状态。这是因为虚假唤醒可能发生(线程被唤醒但条件未成立),且超时后需重新验证条件。 - **步骤3: 调用等待函数**:在循环中调用`pthread_cond_timedwait`,传入已加锁的互斥量超时时间。 - **步骤4: 处理唤醒后逻辑**:唤醒后,线程自动重新加锁互斥量,需再次检查条件(循环中),并执行操作。 - **步骤5: 解锁互斥量**:操作完成后解锁互斥量。 **示例代码(生产者-消费者模型)**: ```c pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; bool data_ready = false; // 共享条件 // 消费者线程(等待数据,超时设为2秒) void* consumer(void* arg) { struct timespec abstime; clock_gettime(CLOCK_REALTIME, &abstime); abstime.tv_sec += 2; // 设置绝对超时时间 pthread_mutex_lock(&mutex); while (!data_ready) { // 循环检查条件 int ret = pthread_cond_timedwait(&cond, &mutex, &abstime); if (ret == ETIMEDOUT) { printf("等待超时,未收到数据\n"); break; } } if (data_ready) { printf("数据已处理\n"); } pthread_mutex_unlock(&mutex); return NULL; } // 生产者线程(改变条件并发送信号) void* producer(void* arg) { pthread_mutex_lock(&mutex); data_ready = true; // 修改共享条件 pthread_cond_signal(&cond); // 唤醒等待线程 pthread_mutex_unlock(&mutex); return NULL; } ``` - **关键注意事项**: - **超时时间设置**:`abstime`必须是绝对时间(使用`clock_gettime(CLOCK_REALTIME, ...)`获取),避免相对时间导致的漂移错误。 - **避免信号丢失**:在调用`pthread_cond_timedwait`前,确保条件检查在循环中,这样即使信号在检查后、等待前发送,线程也不会错过(因为循环会重新检查)[^4]。 - **错误处理**:检查返回值,处理超时或错误。 - **资源管理**:初始化/销毁条件变量互斥量(未在示例中展示,需用`pthread_cond_init`/`pthread_mutex_init`对应销毁函数)。 #### 4. **应用场景** - **超时控制**:如线程等待I/O操作或网络响应时,避免无限阻塞。例如,在实时系统中,线程可设置最大等待时间,超时后执行备选逻辑[^2]。 - **任务调度**:在任务队列中,工作线程使用`pthread_cond_timedwait`等待新任务,若超时则检查空闲状态或执行清理。 - **资源同步**:如数据库连接池中,线程等待可用连接,超时返回错误而非死锁。 - **优势**:相比`sleep`函数,`pthread_cond_timedwait`更精确且可控,因为它与互斥量集成,确保唤醒时数据状态一致[^4]。 #### 5. **常见错误与避免方法** - **虚假唤醒**:所有条件变量等待函数都可能因系统原因无故唤醒线程。解决方案:始终在循环中检查条件(如`while (!condition)`)。 - **信号丢失**:如果信号在等待前发送,线程可能永远阻塞。避免方法:确保信号发送在互斥量保护下,且等待循环覆盖所有执行路径。 - **死锁风险**:忘记解锁互斥量或错误加锁顺序。规则:调用等待函数前必须已加锁,唤醒后自动重新加锁。 - **性能问题**:过度使用条件变量可能增加线程切换。优化:批量处理信号(如`pthread_cond_broadcast`唤醒所有线程)。 #### 总结 条件变量通过“等待-通知”机制提升多线程效率,`pthread_cond_timedwait`扩展了其能力,支持超时处理。正确使用时需结合互斥量、循环检查条件严格错误处理。典型应用包括高并发服务实时系统[^1][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值