为什么Rust RwLock在使用叉子时会出现意外行为?
我看到了一些我无法解释的行为,当我使用锁和叉的时候。基本上,子进程报告一个仍被获取的RwLock,而父进程则没有,即使它们都运行相同的代码路径。我的理解是,子进程应该接收父进程内存空间(包括锁)的独立副本,因此它们应该报告不同的结果是没有意义的 预期的行为是子级和父级都报告“mutex hold:false”。有趣的是,当使用互斥锁而不是RwLock时,这就像预期的那样工作为什么Rust RwLock在使用叉子时会出现意外行为?,rust,multiprocessing,locking,fork,mutex,Rust,Multiprocessing,Locking,Fork,Mutex,我看到了一些我无法解释的行为,当我使用锁和叉的时候。基本上,子进程报告一个仍被获取的RwLock,而父进程则没有,即使它们都运行相同的代码路径。我的理解是,子进程应该接收父进程内存空间(包括锁)的独立副本,因此它们应该报告不同的结果是没有意义的 预期的行为是子级和父级都报告“mutex hold:false”。有趣的是,当使用互斥锁而不是RwLock时,这就像预期的那样工作 我猜你是在Linux系统上运行的。Rust之所以这样做是因为glibc做到了这一点,Rust的rBlock基于glibc
我猜你是在Linux系统上运行的。Rust之所以这样做是因为glibc做到了这一点,Rust的
rBlock
基于glibc在使用Linux系统的glibc上的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;
}
16在我的系统上是EBUSY
glibc发生这种情况的原因是POSIX为rwlocks指定了一个解锁函数,glibc存储当前线程ID以确定当前线程持有的锁是读锁还是写锁。如果当前线程ID等于存储的值,则该线程具有写锁,否则具有读锁。因此,您实际上没有解锁子系统中的任何内容,但很可能损坏了锁中的读卡器计数器
正如注释中提到的,根据POSIX,这是子线程中未定义的行为,因为线程解锁不是持有锁的线程。为了实现这一点,Rust必须像Go一样实现自己的同步原语,这通常是一个主要的可移植性噩梦。工作原理是,这只是未定义的行为:“如果读写锁rBlock不由调用线程持有,则结果未定义。”看起来,即使分叉进程有父级内存的副本,锁的状态中也必须有线程id,从而导致其行为异常。^这似乎是我的答案,再加上这些同步抽象通常构建在操作系统原语()之上的事实,因此可能会泄露实施细节,尤其是在
不安全的情况下。
PARENT mutex held: false
CHILD mutex held: true
#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