简体   繁体   English

尝试在 Arc 中使用互斥锁<mutex<t> &gt; 使用 Condvar </mutex<t>

[英]trying to use the mutex in an Arc<Mutex<T>> with a Condvar

further along in trying to port my c++ timer queue to rust.进一步尝试将我的 c++ 计时器队列移植到 rust。 Reading a lot of other peoples code, the rust library etc I came up with this scheme阅读了很多其他人的代码,rust 库等我想出了这个方案

I have an outer struct that clients use.我有一个客户使用的外部结构。 It is simply a wrapper around an inner implementation.它只是一个内部实现的包装器。 So I have所以我有

type QIFunc = Box<dyn Fn() -> () + Send>;

pub struct TimerQueueItem {
    when: Instant,
    name: String,
    what: QIFunc,
}

struct _TimerQueue {
    running: bool,
    stop: bool,
    condvar: Condvar,
    queue: Vec<TimerQueueItem>,
}

struct TimerQueue {
    inner: Arc<Mutex<_TimerQueue>>,
}

There is a dedicated thread that picks things off this queue and runs them.有一个专门的线程从这个队列中挑选东西并运行它们。 Works fine without the timing, what I need to do is a classic condvar thing没有时间就可以正常工作,我需要做的是经典的 condvar 事情

lock cv -> is queue empty -> wait. else pick item off queue (queue is sorted by due time)
look at item -> is it due yet, yes run it, no lock cv till it is due
while sleeping a new thing gets posted, so notify the cv to wake up thread and look at head of queue again.

I want to use the mutex thats in my Arc<Mutex<_TimerQueue>> since thats the logical queue lock.我想使用Arc<Mutex<_TimerQueue>>中的互斥锁,因为那是逻辑队列锁。 But I cannot work out how to get at it in such a way that it will compile.但是我无法弄清楚如何以可以编译的方式进行处理。

I have this我有这个

impl TimerQueue {
    fn thr(&self) -> () {
        let inner = self.inner.clone();
        thread::spawn(move || {

            loop {
                //let tqi:TimerQueueItem;
                loop {
                  
                    let tqimp = &match inner.lock() {
                        Ok(tqimp) => tqimp,
                        _ => continue,
                    };

                    while !tqimp.stop && tqimp.queue.is_empty() {
                        tqimp.condvar.wait(xxx);
                    }

                    if tqimp.stop {
                        return;
                    }

                    let now = Instant::now();
                    let tqi = &tqimp.queue[0];

                    let due = tqi.when;
                    if due > now {
                        let wait = due - now;
                        tqimp.condvar.wait_timeout(xxx, wait);

                        let tqi = &tqimp.queue[0];
                        let now = Instant::now();
                        let due = tqi.when;
                        if due <= now {
                            (tqi.what)();
                        }
                    } else {
                        let tqi = &tqimp.queue[0];
                        (tqi.what)();
                    
                    }
                }
              //  (tqi.what)();
            }
        });
    }

but cannot work out what I need to put in the cv wait calls (where there is xxx at the moment).但无法弄清楚我需要在 cv 等待调用中添加什么(目前有 xxx)。 I have tried &, * clone,,,,.我试过 &, * 克隆,,,,. I am stuck.我被困住了。 (The commented out code is me trying to pull out the execution of the 'what' to be outside the lock, stuck on that too but I am sure I can work that one out) (注释掉的代码是我试图将“什么”的执行拉出锁外,也卡在了那个上面,但我相信我可以解决这个问题)

EDIT:编辑:

heres the entire source这是整个来源

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

type QIFunc = Box<dyn Fn() -> () + Send>;

struct TimerQueueItem {
    when: Instant,
    name: String,
    what: QIFunc,
}

struct _TimerQueue {
    running: bool,
    stop: bool,
    condvar: Arc<Condvar>,
    queue: Vec<TimerQueueItem>,
}

pub struct TimerQueue {
    inner: Arc<Mutex<_TimerQueue>>,
}




impl TimerQueue {
    fn thr(&self) -> () {
        let inner = self.inner.clone();
        thread::spawn(move || {

            loop {
                //let tqi:TimerQueueItem;
                loop {
                  
                    let mut tqimp = &match inner.lock() {
                        Ok(tqimp) => tqimp,
                        _ => continue,
                    };
                    let cv = tqimp.condvar.clone();
                    while !tqimp.stop && tqimp.queue.is_empty() {
                        cv.wait(xxx);
                    }

                    if tqimp.stop {
                        return;
                    }

                    let now = Instant::now();
                    let tqi = &tqimp.queue[0];

                    let due = tqi.when;
                    if due > now {
                        let wait = due - now;
                        cv.wait_timeout(xxx, wait);

                        let tqi = &tqimp.queue[0];
                        let now = Instant::now();
                        let due = tqi.when;
                        if due <= now {
                            (tqi.what)();
                        }
                    } else {
                        let tqi = &tqimp.queue[0];
                        (tqi.what)();
                    
                    }
                }
              //  (tqi.what)();
            }
        });
    }

    fn set(&self, f: QIFunc, n: String, when: Instant) {
        let qi = TimerQueueItem {
            what: f,
            name: n,
            when: when,
        };

        let mut inner = self.inner.lock().unwrap();
        inner.queue.push(qi);
        inner.queue.sort_by_key(|k| k.when);
    }
    fn new() -> TimerQueue {
        let inner = Arc::new(Mutex::new(_TimerQueue {
            queue: Vec::new(),
            running: false,
            stop: false,
            condvar: Arc::new(Condvar::new()),
        }));
        TimerQueue { inner: inner }
    }
}

fn main() {
    let x = || {
        println!("hello");
    };
    let y = || {
        println!("hello2");
    };

    let tq = TimerQueue::new();

    tq.set(Box::new(x), String::from("yo"), Instant::now());
    tq.set(Box::new(y), String::from("yo"), Instant::now());

    thread::sleep_ms(10000);
}

Normally you should put the MutexGuard that was returned by lock , which in your case is tqimp .通常,您应该放置由lock返回的MutexGuard ,在您的情况下是tqimp But you can't because that would require a mutable borrow (in the parameter) at the same time as another borrow (to access tqimp.condvar ).但是你不能,因为这需要一个可变的借用(在参数中)和另一个借用(访问tqimp.condvar )。 You will need to either store the CondVar outside the queue or to use an Arc :您需要将CondVar存储在队列之外或使用Arc

struct _TimerQueue {
    running: bool,
    stop: bool,
    condvar: Arc<Condvar>,
    queue: Vec<TimerQueueItem>,
}

Then when you want to use it:然后当你想使用它时:

let mut tqimp = match inner.lock() { // tqimp can't be a reference
        Ok(tqimp) => tqimp,
        _ => continue,
    };
let condvar = tqimp.condvar.clone();
tqimp = condvar.wait (tqimp).unwrap();

Note that condvar.wait consumes tqimp and returns a new MutexGuard (because it releases the mutex until the condition triggers, then locks it again before returning).注意condvar.wait消耗tqimp并返回一个新的MutexGuard (因为它释放互斥锁直到条件触发,然后在返回之前再次锁定它)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM