操作系统线程调度因素是否会影响使用 std::sync::Mutex 与 tokio::sync::Mutex 的决定?

问题描述 投票:0回答:1

tokio::sync::mutex
文档提到以下内容:

与流行的看法相反,在异步代码中使用标准库中的普通互斥体是可以的,而且通常是首选。

此评论由this更详细地制定,所以答案:

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

这似乎描绘了使用单线程 tokio 运行时的完整情况。然而,如果使用多线程运行时,可能会出现这样一种情况:操作系统线程 1 上的任务 A 持有同步互斥锁,并且其线程被操作系统抢占,此时线程 2 上的任务 B 可能会被操作系统抢占。尝试获取互斥体的计划循环和废物循环。这对于 tokio 互斥体来说不是问题,因为任务 B 会调用

await
并且可以用不同的任务替换。

为什么同步互斥体的这一潜在缺点没有被更频繁地提及? 与使用异步互斥体的开销相比,这种开销来源通常是微不足道的吗?

rust mutex rust-tokio
1个回答
0
投票

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

更多:

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

另一个需要考虑的因素是

std::sync::Mutex

 由操作系统原语锁定调用支持,而不仅仅是在用户空间 AFAIK 中。这意味着即使线程被抢占,操作系统也知道该线程有它想要的进度并且它持有锁。如果另一个线程被同一个锁阻塞,操作系统就会意识到这一点。这可能会影响操作系统的调度,从而为持有锁的线程提供更高的优先级以再次开始。这可以减少使用 
std::sync::Mutex
 时有争议的线程的影响。

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

© www.soinside.com 2019 - 2024. All rights reserved.