将锁包装到范围内是否会改变锁何时被释放?

huangapple go评论83阅读模式
英文:

Does wrapping lock into scope change when lock will be released?

问题

In code

fn do_something_under_lock(some_bool_mutex: &Mutex<bool>) {
    do_something_before_lock();

    let some_bool = some_bool_mutex.lock().unwrap();
    do_something_with_some_bool(some_bool);

    do_something_after_lock();
}

锁将仅在do_something_after_lock();之后释放吗?(或者编译器是否可以证明我们不需要长时间的锁并减少它?)

如果锁只在do_something_after_lock();之后释放,我们是否可以通过将lock()包装到自己的作用域中提前释放锁,就像这样:

fn do_something_under_lock_in_scope(some_bool_mutex: &Mutex<bool>) {
    do_something_before_lock();

    {
        let some_bool = some_bool_mutex.lock().unwrap();
        do_something_with_some_bool(some_bool);
    }

    do_something_after_lock();
}

将其包装到作用域中是否是告诉编译器我们的意图的最佳方式,或者我们应该使用其他方式,比如drop或其他方法?

英文:

In code

fn do_something_under_lock(some_bool_mutex: &amp;Mutex&lt;bool&gt;) {
    do_something_before_lock();

    let some_bool = some_bool_mutex.lock().unwrap();
    do_something_with_some_bool(some_bool);

    do_something_after_lock();
}

will lock be released only after do_something_after_lock();?(or maybe compiler can prove that we don't need long lock time and decrease it?)

If lock will be released only after do_something_after_lock();, can we release lock earlier by wrapping lock() into own scope, like

fn do_something_under_lock_in_scope(some_bool_mutex: &amp;Mutex&lt;bool&gt;) {
    do_something_before_lock();

    {
        let some_bool = some_bool_mutex.lock().unwrap();
        do_something_with_some_bool(some_bool);
    }

    do_something_after_lock();
}

Is wrapping into scope the best way to tell compiler what we want, or should we use some other way, like drop or something?

答案1

得分: 3

锁将在 do_something_after_lock() 后释放。这被视为可观察的行为,因此编译器不允许更改它。

将锁包装在作用域中并使用 drop() 都可以。我更喜欢 drop() 版本,但这是主观的。但需要注意的一点是,在异步函数中,即使你使用 drop() 释放了锁(锁将会被释放,但编译器仍将其类型视为生成器类型的一部分),所以如果锁不能在 .await 点之间保持(例如因为它不是 Send),你必须使用一个块并且 drop() 是不够的。例如,以下代码会出错:

async fn foo(mutex: &Mutex<i32>) {
    let _guard = mutex.lock().unwrap();
    drop(_guard);
    
    tokio::time::sleep(Duration::from_secs(1)).await;
}

tokio::spawn(foo(mutex));

你需要使用一个块:

async fn foo(mutex: &Mutex<i32>) {
    {
        let _guard = mutex.lock().unwrap();
    }
    
    tokio::time::sleep(Duration::from_secs(1)).await;
}

tokio::spawn(foo(mutex));

预计将在未来的 Rust 版本中改进

英文:

The lock will be released after do_something_after_lock(). This is considered observable behavior, and therefore, the compiler is not allowed to change that.

Wrapping the lock in scope and using drop() are both fine. I prefer the drop() version, but this is subjective. One thing that is important to note, however, is that in async functions the lock is considered alive even if you drop() it (it will be released, but the compiler will still consider its type as part of the generator type), so if the lock must not be held across .await points (for example because it is not Send), you must use a block and drop() is not enough. For instance, the following code errs:

async fn foo(mutex: &amp;Mutex&lt;i32&gt;) {
    let _guard = mutex.lock().unwrap();
    drop(_guard);
    
    tokio::time::sleep(Duration::from_secs(1)).await;
}

tokio::spawn(foo(mutex));

You need to use a block:

async fn foo(mutex: &amp;Mutex&lt;i32&gt;) {
    {
        let _guard = mutex.lock().unwrap();
    }
    
    tokio::time::sleep(Duration::from_secs(1)).await;
}

tokio::spawn(foo(mutex));

This is expected to improve in future Rust versions.

huangapple
  • 本文由 发表于 2023年7月6日 18:07:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76627706.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定