tokio::sync::mutex documentation人提到了以下几点:

与流行的看法相反,在异步代码中使用标准库中的普通Mutex是可以的,而且通常更受欢迎.

这条 comments 是由this人更详细地写出来的,所以请回答:

  • 如果需要在.aWait调用上持有锁,请使用异步互斥锁.在使用线程安全的future 时,编译器通常会拒绝这一点,因为大多数同步互斥锁不能被发送到另一个线程.
  • 如果你的锁是有争议的(即,如果你希望互斥锁在你需要的时候已经被锁定),你应该使用异步互斥锁.当将多个任务同步到池或有界队列中时,可能会发生这种情况.
  • 如果您有复杂的和/或计算繁重的更新,则无论如何都应该将这些更新移到阻塞池中,在那里您将使用同步互斥.

在使用单线程Tokio运行时的情况下,这似乎描绘了一幅完整的画面.然而,如果使用多线程运行时,可能会出现这样的情况:OS线程1上的任务A持有同步互斥锁,并且其线程被操作系统抢占,此时线程2上的任务B可能被调度并且浪费试图获取互斥锁的周期.这对于Tokio互斥来说不是问题,因为任务B将调用await,并且可以用不同的任务来替换.

为什么没有更多地提到同步互斥锁的这一潜在缺点呢? 与使用异步互斥锁的开销相比,这种开销来源通常微不足道吗?

推荐答案

这真的没有什么不同.如果您的锁不具有争议性并且持有时间不长,那么无论如何它都不太可能出现在这种场景中,与异步互斥相比,同步互斥通常会产生更好的整体性能,即使这种情况偶尔会发生.

更多信息:

我认为要认识到的一个关键问题是,操作系统重新调度线程all the time.即使不涉及互斥锁,您的异步任务也会不断地被微暂停,因为操作系统试图处理数百或数千个线程,这些线程正在争夺少数逻辑线程.因此,如果一个带锁的线程被暂停,另一个需要锁的线程也会被有效地暂停,但这与发生的情况相差不远.

另一个要考虑的因素是,std::sync::Mutex是由操作系统原语锁定调用支持的,而不仅仅是在用户空间AFAIK中.这意味着,即使线程被抢占,OS也知道线程有它想要的进程,并且它持有锁.如果另一个线程被相同的锁阻塞,操作系统就会意识到这一点.这可能会影响操作系统的调度,使持有锁的线程具有更高的优先级才能再次运行.这减少了使用std::sync::Mutex时争用线程的影响.

最重要的是,您应该始终进行基准测试,看看它是否会对您的用例产生影响.

Rust相关问答推荐

MacOS(AARCH64)上Ghidra中的二进制补丁导致进程终止

Rust:跨多个线程使用hashmap Arc和rwlock

为什么我不能从带有字符串的 struct 的引用迭代器中收集VEC<;&;str&>?

如何将`Join_all``Vec<;Result<;Vec<;Foo&>;,Anywhere::Error&>;`合并到`Result<;Vec<;Foo&>;,Anywhere::Error&>;`

亚性状上位性状上的 rust 病伴生型界限

有没有办法在Rust中配置常量变量的值?

在Rust中显式装箱受生存期限制的转换闭包

无法将记录器向下转换回原始 struct

创建Rust中元对象协议的动态对象 Select /重新分配机制

在文件链实施中绕过borrow 判断器

如何对一个特征的两个实现进行单元测试?

Rust:为什么 &str 不使用 Into

为什么切片时需要参考?

如何为整数切片定义一个带有额外函数的特性别名?

Rust中的位移操作对范围有什么影响?

pyO3 和 Panics

从 Axum IntoResponse 获取请求标头

If let expression within .iter().any

为什么分配对变量的引用使我无法返回它

通用类型,不同于输入类型,作为函数的返回值