gpt4 book ai didi

rust - 为什么 Rust RwLock 使用 fork 会出现意外行为?

转载 作者:行者123 更新时间:2023-12-03 11:29:58 25 4
gpt4 key购买 nike

当我使用 RwLock 和 fork 时,我看到了一些无法解释的行为。基本上,子进程报告仍获得了 RwLock,而父进程则不会,即使它们都运行相同的代码路径。我的理解是子进程应该接收父进程内存空间的独立副本,包括锁,所以他们应该报告不同的结果是没有意义的。

预期的行为是 child 和 parent 都报告“互斥锁:假”。有趣的是,当使用互斥体而不是 RwLock 时,这按预期工作。

Rust Playground link

use libc::fork;
use std::error::Error;
use std::sync::RwLock;

fn main() -> Result<(), Box<dyn Error>> {
let lock = RwLock::new(());

let guard = lock.write();
let res = unsafe { fork() };
drop(guard);

match res {
0 => {
let held = lock.try_write().is_err();
println!("CHILD mutex held: {}", held);
}
_child_pid => {
let held = lock.try_write().is_err();
println!("PARENT mutex held: {}", held);
}
}
Ok(())
}

输出:
PARENT mutex held: false
CHILD mutex held: true

最佳答案

我假设您在这里运行的是 Linux 系统。 Rust 这样做是因为 glibc 这样做,而 Rust 的 RwLock基于 glibc 在使用 glibc 的 Linux 系统上的 pthreads 实现。

您可以使用等效的 C 程序确认此行为:

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

int main(void)
{
pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER;

pthread_rwlock_wrlock(&lock);
pid_t pid = fork();
int res = pthread_rwlock_unlock(&lock);
int res2 = pthread_rwlock_trywrlock(&lock);

printf("%s unlock_errno=%d trywrlock_errno=%d\n", (pid == 0) ? "child" : "parent", res, res2);
return 0;
}

打印以下内容:
parent unlock_errno=0 trywrlock_errno=0
child unlock_errno=0 trywrlock_errno=16

16 是 EBUSY在我的系统上。

glibc 发生这种情况的原因是 POSIX 为 rwlocks 指定了一个解锁函数,glibc 存储当前线程 ID 来确定当前线程持有的锁是读锁还是写锁。如果当前线程 ID 等于存储的值,则该线程具有写锁,否则具有读锁。所以你实际上并没有解锁 child 中的任何东西,但你可能已经破坏了锁中的阅读器计数器。

正如评论中提到的,根据 POSIX,这是子进程中未定义的行为,因为线程解锁不是持有锁的线程。为了让它起作用,Rust 必须像 Go 一样实现自己的同步原语,这通常是一个主要的可移植性噩梦。

关于rust - 为什么 Rust RwLock 使用 fork 会出现意外行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61976745/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com