【问题标题】:How do I create a mutex with two condvars?如何创建具有两个 condvar 的互斥锁?
【发布时间】:2020-09-29 21:36:54
【问题描述】:

我想在 Rust 中构建一个单生产者多消费者示例,其中生产者的未完成项不得超过 10 个。我在 C 中建模了一个使用互斥锁和两个 condvar 的解决方案。一个 condvar 是在没有东西可消费时等待消费者,一个 condvar 是在未消费项目数大于 10 时等待生产者。C 代码如下。

据我从 Rust 文档中了解到,std::sync::Mutexstd::sync::Condvar 之间必须存在 1-1 连接,因此我无法准确翻译我的 C 解决方案。

是否有其他方法可以使用std::sync::Mutexstd::sync::Condvar 在 Rust 中实现相同的目的(我看不到)。

#define _GNU_SOURCE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

//
// This is a simple example of using a mutex and 2 condition variables to
// sync a single writer and multiple readers interacting with a bounded (fixed max size) queue
//
// in this toy example a queue is simulated by an int counter n_resource
//

int n_resource;
pthread_cond_t rdr_cvar;
pthread_cond_t wrtr_cvar;
pthread_mutex_t mutex;

void reader(void* data)
{
    long id = (long)data;
    for(;;) {

        pthread_mutex_lock(&mutex);
        while (n_resource <= 0) {
            pthread_cond_wait(&rdr_cvar, &mutex);
        }
        printf("Reader %ld n_resource = %d\n", id, n_resource);
        --n_resource;
        // if there are still things to read - singla one reader
        if(n_resource > 0) {
            pthread_cond_signal(&rdr_cvar);
        }
        // if there is space for the writer to add another signal the writer
        if(n_resource < 10) {
            pthread_cond_signal(&wrtr_cvar);
        }
        pthread_mutex_unlock(&mutex);
    }
}
void writer(void* data)
{
    for(;;) {

        pthread_mutex_lock(&mutex);
        printf("Writer before while n_resource %d \n", n_resource);
        while (n_resource > 10) {
            pthread_cond_wait(&wrtr_cvar, &mutex);
        }
        printf("Writer after while n_resource %d \n", n_resource);

        ++n_resource;
        // if there is something for a reader to read signal one of the readers.
        if(n_resource > 0) {
            pthread_cond_signal(&rdr_cvar);
        }
        pthread_mutex_unlock(&mutex);
    }
}

int main()
{
    pthread_t rdr_thread_1;
    pthread_t rdr_thread_2;
    pthread_t wrtr_thread;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&rdr_cvar, NULL);
    pthread_cond_init(&wrtr_cvar, NULL);
    pthread_create(&rdr_thread_1, NULL, &reader, (void*)1L);
    pthread_create(&rdr_thread_2, NULL, &reader, (void*)2L);
    pthread_create(&wrtr_thread, NULL, &writer, NULL);
    pthread_join(wrtr_thread, NULL);
    pthread_join(rdr_thread_1, NULL);
    pthread_join(rdr_thread_2, NULL);
}

【问题讨论】:

  • 看起来您的问题可能会被Buffer in Rust with Mutex and Condvar 的答案所回答,这表明使用两个Condvars 和一个Mutex。如果没有,请edit您的问题解释差异。否则,我们可以将此问题标记为已回答。

标签: rust mutex condition-variable


【解决方案1】:

虽然CondVar 只需要与一个Mutex 相关联,但Mutex 不必只与一个CondVar 相关联。

例如,以下代码似乎可以正常工作 - 您可以在 playground 上运行它。

use std::sync::{Arc, Condvar, Mutex};
use std::thread;

struct Q {
    rdr_cvar: Condvar,
    wrtr_cvar: Condvar,
    mutex: Mutex<i32>,
}

impl Q {
    pub fn new() -> Q {
        Q {
            rdr_cvar: Condvar::new(),
            wrtr_cvar: Condvar::new(),
            mutex: Mutex::new(0),
        }
    }
}

fn writer(id: i32, qq: Arc<Q>) {
    let q = &*qq;
    for i in 0..10 {
        let guard = q.mutex.lock().unwrap();
        let mut guard = q.wrtr_cvar.wait_while(guard, |n| *n > 3).unwrap();

        println!("{}: Writer {} n_resource = {}\n", i, id, *guard);
        *guard += 1;

        if *guard > 0 {
            q.rdr_cvar.notify_one();
        }
        if *guard < 10 {
            q.wrtr_cvar.notify_one();
        }
    }
}

fn reader(id: i32, qq: Arc<Q>) {
    let q = &*qq;
    for i in 0..10 {
        let guard = q.mutex.lock().unwrap();
        let mut guard = q.rdr_cvar.wait_while(guard, |n| *n <= 0).unwrap();

        println!("{} Reader {} n_resource = {}\n", i, id, *guard);
        *guard -= 1;

        if *guard > 0 {
            q.rdr_cvar.notify_one();
        }
        if *guard < 10 {
            q.wrtr_cvar.notify_one();
        }
    }
}

fn main() {
    let data = Arc::new(Q::new());
    let data2 = data.clone();

    let t1 = thread::spawn(move || writer(0, data2));
    let t2 = thread::spawn(move || reader(1, data));

    t1.join().unwrap();
    t2.join().unwrap();
}

【讨论】:

  • 这是否意味着您同意重复?
  • 我认为副本确实解决了周围的问题 --- 实施生产者 - 消费者队列 --- 但没有直接解决这个问题的核心误解,即“据我所知它来自 Rust 文档,std::sync::Mutex 和 std::sync::Condvar 之间必须存在 1-1 连接,因此我无法准确翻译我的 C 解决方案。” - 我的第一句话指出的不正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-05
  • 1970-01-01
  • 2013-03-30
  • 2018-05-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多