Linux系统编程之条件变量

什么是条件变量?

在Linux中,条件变量(Condition Variable)是一种用于线程同步的机制,通常与互斥锁(Mutex)一起使用。条件变量提供了一种线程间的通信机制,允许一个线程等待另一个线程满足某个条件后再继续执行。 条件变量的基本概念是,一个线程在某个条件不满足时可以通过条件变量等待,而另一个线程在满足条件时可以通过条件变量通知等待的线程。这样可以在多线程环境中有效地实现线程间的协同工作。 在Linux中,条件变量通常使用以下两个函数进行操作:

  1. 初始化条件变量: 这个函数用于初始化条件变量。cond 是指向要初始化的条件变量的指针,attr 是条件变量的属性,通常设置为NULL表示使用默认属性。

    int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
    
    
  2. 等待条件变量: 这个函数使调用线程等待条件变量 cond 被其他线程通知。在等待之前,需要先锁定互斥锁 mutex,等待时会释放锁并阻塞线程,当其他线程通过 pthread_cond_signalpthread_cond_broadcast 发送通知时,等待的线程将被唤醒,并在重新获得锁后继续执行。

    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
    
    
  3. 发送通知: 这个函数用于向等待在条件变量上的一个线程发送通知,唤醒其中一个等待的线程。

    int pthread_cond_signal(pthread_cond_t *cond);
    
    
  4. 广播通知: 这个函数用于向等待在条件变量上的所有线程发送通知,唤醒所有等待的线程。 使用条件变量的典型场景是在多线程环境中实现线程之间的协同工作,例如生产者-消费者问题。条件变量和互斥锁一起使用,确保在某个条件满足时线程能够正确地等待和唤醒。

    int pthread_cond_broadcast(pthread_cond_t *cond);
    
    

    注意:条件变量本身不是锁,但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会和的场所。

    主要应用函数:

    1. pthread_cond_init函数

    pthread_cond_init 函数用于初始化条件变量:

    int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
    
    
    • cond:指向要初始化的条件变量的指针。
    • attr:条件变量的属性,通常设置为 NULL 表示使用默认属性。如果需要自定义属性,可以使用 pthread_condattr_t 类型的变量传递相关属性信息。 这个函数用于初始化条件变量,创建一个新的条件变量对象并进行初始化。在使用条件变量之前,必须先调用此函数进行初始化。如果初始化成功,将返回0;否则返回相应的错误码。
    1. pthread_cond_destroy函数

    pthread_cond_destroy 函数用于销毁条件变量对象:

    int pthread_cond_destroy(pthread_cond_t *cond);
    
    
    • cond:指向要销毁的条件变量的指针。 这个函数用于释放条件变量占用的资源,销毁条件变量对象。在条件变量不再需要使用时,应该调用此函数进行清理,以防止资源泄漏。如果销毁成功,将返回0;否则返回相应的错误码。 需要注意的是,调用 pthread_cond_destroy 之前,条件变量必须处于未被等待状态,并且没有线程在条件变量上等待。否则,销毁操作可能会导致未定义的行为。通常,在销毁条件变量之前,需要确保没有线程在条件变量上等待,并且相关的互斥锁也已经被销毁。
    1. pthread_cond_wait函数 (重要!!)

    阻塞等待一个条件变量

    int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

    函数作用:

    .1 阻塞等待条件变量cond满足

    .2释放已掌握的互斥锁相当于 pthread_mutex_unlock(&mutex);

    .1 .2俩步为一个原子操作。

    .3 当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁 pthread_mutex_lock(&mutex);

Linux系统编程之条件变量_第1张图片

详细解释:

pthread_cond_wait 函数用于在条件变量上等待:

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

  • cond:指向要等待的条件变量的指针。
  • mutex:指向与条件变量关联的互斥锁的指针。在调用该函数前,必须先锁定这个互斥锁。 这个函数使调用线程等待条件变量 cond 被其他线程通过 pthread_cond_signalpthread_cond_broadcast 发送通知。在等待之前,需要先锁定互斥锁 mutex,等待时会释放锁并阻塞线程。当其他线程通过条件变量发送通知时,等待的线程将被唤醒,并在重新获得锁后继续执行。 需要注意的是,在等待之前,线程必须已经获得与条件变量关联的互斥锁,否则会出现未定义的行为。在等待时,互斥锁会被释放,允许其他线程访问共享资源。被唤醒后,线程会重新获得互斥锁,并继续执行。
  1. pthread_cond_timewait函数

pthread_cond_timedwait 函数是带有超时的条件变量等待函数:

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

  • cond:指向要等待的条件变量的指针。
  • mutex:指向与条件变量关联的互斥锁的指针。在调用该函数前,必须先锁定这个互斥锁。
  • abstime:指定等待的绝对时间,是一个 struct timespec 结构体,表示等待的截止时间。 这个函数使调用线程等待条件变量 cond 被其他线程通过 pthread_cond_signalpthread_cond_broadcast 发送通知,或者等待超过指定的超时时间。在等待之前,需要先锁定互斥锁 mutex,等待时会释放锁并阻塞线程。如果在超时前接收到通知,则线程将被唤醒,并在重新获得锁后继续执行。如果超时时间到达而没有接收到通知,函数将返回逾时错误。 需要注意的是,abstime 参数表示的是绝对时间,通常通过 clock_gettime 函数来获取。在使用该函数时,确保互斥锁 mutex 已经被锁定,并在函数返回后重新获得互斥锁。
  1. pthread_cond_signal函数

pthread_cond_signal 函数用于向等待在条件变量上的一个线程发送信号,通知其可以继续执行:

int pthread_cond_signal(pthread_cond_t *cond);

  • cond:指向要发送信号的条件变量的指针。 这个函数用于唤醒等待在条件变量 cond 上的一个线程。被唤醒的线程将在重新获得与条件变量关联的互斥锁后继续执行。如果有多个线程在条件变量上等待,pthread_cond_signal 只会唤醒其中的一个线程,具体唤醒哪个线程是不确定的。 需要注意的是,在调用 pthread_cond_signal 之前,必须确保已经获得与条件变量关联的互斥锁。发送信号的目的是通知等待的线程条件可能已经发生变化,但并不提供具体的条件信息,等待的线程仍然需要重新检查条件。 通常情况下,pthread_cond_signal 配合互斥锁一起使用,确保在发出信号和等待线程重新检查条件之间能够正确地同步和保护共享资源。
  1. pthread_cond_broadcast函数

pthread_cond_broadcast 函数用于向等待在条件变量上的所有线程发送信号,通知它们可以继续执行:

int pthread_cond_broadcast(pthread_cond_t *cond);

  • cond:指向要发送信号的条件变量的指针。 这个函数用于唤醒等待在条件变量 cond 上的所有线程。被唤醒的线程将在重新获得与条件变量关联的互斥锁后继续执行。与 pthread_cond_signal 不同的是,pthread_cond_broadcast 会唤醒所有等待线程,而不仅仅是一个。 需要注意的是,在调用 pthread_cond_broadcast 之前,必须确保已经获得与条件变量关联的互斥锁。发送广播的目的是通知所有等待的线程条件可能已经发生变化,但并不提供具体的条件信息,等待的线程仍然需要重新检查条件。 通常情况下,pthread_cond_broadcast 配合互斥锁一起使用,确保在发出广播和等待线程重新检查条件之间能够正确地同步和保护共享资源。

以上六个函数的返回值都是:成功返回0,失败返回错误号。

pthread_cond_t类型 用于定义条件变量。

pthread_cond_t cond;

初始化条件变量:

  1. pthread_cond_init(&cond,NULL); 动态初始化
  2. pthread_cond_t cond=PTHREAD_COND_INITIALIZER 静态初始化

Linux系统编程之条件变量_第2张图片Linux系统编程之条件变量_第3张图片Linux系统编程之条件变量_第4张图片Linux系统编程之条件变量_第5张图片Linux系统编程之条件变量_第6张图片

条件变量signal注意事项:

  1. 在等待前必须锁定互斥锁: 在调用 pthread_cond_wait 函数等待条件变量前,必须先锁定相应的互斥锁(mutex)。等待操作将在释放锁的情况下进行,这是为了避免在等待时其他线程修改了共享资源。
  2. 等待时会释放锁: 调用 pthread_cond_wait 时,等待线程会释放之前锁定的互斥锁,这使得其他线程有机会修改共享资源。等待线程在被唤醒后会重新获得互斥锁。
  3. 唤醒时必须重新锁定互斥锁: 唤醒等待在条件变量上的线程后,被唤醒的线程需要重新锁定互斥锁,然后检查条件是否满足。这是为了确保在继续执行之前能够安全地访问共享资源。
  4. 条件变量的等待可能出现虚假唤醒: 在某些情况下,等待在条件变量上的线程可能会在没有收到信号的情况下被唤醒(虚假唤醒)。因此,检查条件是否满足时应该使用循环来防止虚假唤醒。
  5. 使用广播(broadcast)通知: 在某些情况下,使用 pthread_cond_broadcastpthread_cond_signal 更合适。广播通知会唤醒所有等待在条件变量上的线程,而不仅仅是一个。这对于确保所有等待线程都有机会检查条件是否满足是有帮助的。 总体而言,合理地使用条件变量能够实现线程之间的协同工作,确保在共享资源的访问中正确地进行同步和通信。

条件变量broadcast注意事项:

pthread_cond_broadcast函数,需要注意以下几个重要的事项:

  1. 谨慎使用广播通知: 使用广播通知时,会唤醒所有等待在条件变量上的线程,而不仅仅是一个。在某些情况下,这可能是必要的,但在其他情况下可能会导致性能问题。因此,应该谨慎使用广播通知,确保它的使用是合理且必要的。
  2. 避免过度使用广播: 过度使用广播通知可能会引起资源争夺和性能下降。如果只有一个线程能够满足条件,而其他线程无法满足条件,那么使用pthread_cond_signal可能更为合适。
  3. 广播通知的适用场景: 广播通知适用于多个等待线程需要同时得到通知的情况,例如当共享资源状态发生改变,所有等待线程都需要重新检查条件时。
  4. 检查条件是否满足: 被唤醒的线程在重新获得互斥锁后,应该再次检查条件是否满足。这是为了防止虚假唤醒和确保在继续执行之前能够安全地访问共享资源。
  5. 结合互斥锁使用: 广播通知通常与互斥锁一起使用,确保在发出通知和等待线程重新检查条件之间能够正确地同步和保护共享资源。

你可能感兴趣的:(Linux,开发语言,linux)