Mutex是什么,有什么作用
-
在编程中,特别是并发编程中,
Mutex(Mutual Exclusion,互斥锁)是一种用于防止多个线程同时访问共享资源的机制。它确保在任意时刻,最多只有一个线程可以持有锁,从而保护临界区内的数据免受数据竞争的影响。 -
主要功能和用途
- 线程同步:
Mutex用于在线程之间同步访问共享资源。例如,在多线程程序中,如果多个线程同时读取和写入共享数据,可能会导致数据不一致或损坏。Mutex确保每次只有一个线程能够访问共享资源,从而防止数据竞争。 - 保护临界区: 临界区是指程序中访问共享资源的代码部分。
Mutex可以保护这些临界区,确保在一个线程完成对共享资源的访问之前,其他线程无法进入该临界区。 - 提高数据安全性: 通过使用
Mutex,可以确保共享资源在多线程环境中保持一致性和完整性,从而提高数据安全性。
-
在Rust中简单使用Mutex
use std::sync::{Arc, Mutex, MutexGuard}; use std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num: MutexGuard<i32> = counter.lock().unwrap(); *num += 1; });// num会被自动drop释放掉资源 handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *counter.lock().unwrap()); }
JS中的数据竞争
JS虽然是一门单线程语言,但是在异步场景中可能也会产生数据竞争
let sharedVariable = 0;
async function incrementVariable () {
const oldValue = sharedVariable;
await new Promise((resolve) => setTimeout(resolve, Math.random() * 1000)); // 模拟异步操作
sharedVariable = oldValue + 1;
console.log(`Incremented shared variable to ${sharedVariable}`);
}
async function main () {
const tasks: Promise<void>[] = [];
for (let i = 0; i < 5; i++) {
tasks.push(incrementVariable());
}
await Promise.all(tasks);
console.log(`Final shared variable value: ${sharedVariable}`);
}
main();
结果如下:
$ bun index.ts
Incremented shared variable to 1
Incremented shared variable to 1
Incremented shared variable to 1
Incremented shared variable to 1
Incremented shared variable to 1
Final shared variable value: 1
其实如果每个task依赖顺序,正常的解决方案来说是不应该使用Promise.all来处理的,直接一个一个遍历即可
for ( let i = 0; i < 5; i++ ) {
await incrementVariable();
}
另外一种方法就是实现一个简单的Mutex
GPT提示是在竞争激烈时, 理论上锁会更快,for循环会慢一些, 但是简单实测了一下,依次迭代的速度是快于下面使用的Mutex的,怀疑是锁的开销比较大导致的
使用JS实现一个简单的Mutex
- 实现
Mutex
class Mutex<T> {
private locked: boolean = false
private waitingQueue: Array<() => void> = []
value: T
constructor(value: T) {
this.locked = false;
this.waitingQueue = [];
this.value = value;
}
async lock (): Promise<MutexGuard<T>> {
while (this.locked) {
await new Promise<void>((resolve) => this.waitingQueue.push(resolve));
}
this.locked = true;
return new MutexGuard(this);
}
unlock (): void {
if (!this.locked) {
throw new Error("Mutex is not locked");
}
this.locked = false;
if (this.waitingQueue.length > 0) {
const resolve = this.waitingQueue.shift();
resolve!();
}
}
}
- 实现
MutexGuard
class MutexGuard<T> {
private mutex: Mutex<T>
constructor(mutex: Mutex<T>) {
this.mutex = mutex;
}
async lock (): Promise<MutexGuard<T>> {
await this.mutex.lock();
return this;
}
unlock (): void {
this.mutex.unlock();
}
get value (): T {
return this.mutex.value;
}
set value (newValue: T) {
this.mutex.value = newValue;
}
}
- 使用
Mutex和MutexGuard
async function main () {
const counter = new Mutex(0);
async function incrementCounter () {
const guard = await counter.lock();
try {
const oldValue = guard.value;
await new Promise<void>((resolve) => setTimeout(resolve, Math.random() * 1000));
guard.value = oldValue + 1;
console.log(`Incremented counter to ${guard.value}`);
} finally {
guard.unlock();
}
}
const tasks: Array<Promise<void>> = [];
for (let i = 0; i < 5; i++) {
tasks.push(incrementCounter());
}
await Promise.all(tasks);
console.log(`Final counter value: ${(await counter.lock()).value}`);
}
main()
- 输出
$ bun index.ts
Incremented counter to 1
Incremented counter to 2
Incremented counter to 3
Incremented counter to 4
Incremented counter to 5
Final counter value: 5