2016-09-26 96 views
0

我在Rust中移植我的C++象棋引擎。我有一个大的哈希表在搜索线程之间共享,并且在C++版本中这个表是无锁的;共享读/写访问没有互斥。如果你有兴趣,这里是the theory在Rust中的线程之间共享无锁资源

在这段代码中的锈版本,它工作正常,但使用的是Mutex

let shared_hash = Arc::new(Mutex::new(new_hash())); 

for _ in 0..n_cpu { 
    println!("start thread"); 
    let my_hash = shared_hash.clone(); 
    thread_pool.push(thread::spawn(move || { 
     let mut my_hash = my_hash.lock().unwrap(); 
     let mut search_engine = SearchEngine::new(); 
     search_engine.search(&mut myhash); 
    })); 
} 

for i in thread_pool { 
    let _ = i.join(); 
} 

我怎么能线程之间共享该表不互斥?

+2

我不确定这是否有适当的答案。如果数据结构不需要锁定,那么您将不需要互斥锁。如果是这样,你*需要一个互斥体,并且试图避免使用互斥体是不安全的。我不知道是否有一个已经存在的适用的解决方案,因为除了“无互斥”之外,您还没有指出您正在尝试做什么。如果没有,那么这就转化为“推荐一个图书馆”(可能因为过于宽泛而被封闭),或者“我怎么写一个无锁的数据结构”(它也可能太宽泛) 。 –

+0

此外,翻阅该链接(我没有时间阅读整篇文章以尝试回答一个模糊的问题),我想知道为什么你不能只使用'Cell '。但是我不知道'new_hash()'做了什么,所以\ *耸耸肩* * –

+2

正如Matthieu M.回答,您需要使您的数据结构实现'Sync',这表明它可以安全地在线程之间共享。这将问题转变为真正的问题:“我如何在Rust中编写**特殊**无锁数据结构?”。要做到这一点,你将*可能*需要编写不安全的代码。看起来,所讨论的散列表需要某些**硬件**先决条件(例如,64位存储器到内存是自然原子的)和**软件**先决条件(例如,您正在存储两个64位值)。这似乎表明数据结构不可移植。 – Shepmaster

回答

4

很简单,实际上:如果底层结构已经为Sync,则不需要Mutex

就你而言,例如atomics结构的数组就可以工作。你可以找到Rust可用的atomics here

-1

至于建议,我写了一个FakeMutex实现Sync,但并没有真正锁定的哈希表:

use core::cell::UnsafeCell; 
use core::marker::Sync; 
use core::ops::{Deref, DerefMut}; 

pub struct FakeMutex<T> { 
    data: UnsafeCell<T>, 
} 

pub struct FakeMutexGuard<'a, T: 'a> { 
    data: &'a mut T, 
} 

unsafe impl<T> Sync for FakeMutex<T> {} 

impl<T> FakeMutex<T> { 
    pub fn new(user_data: T) -> FakeMutex<T> { 
     FakeMutex { 
      data: UnsafeCell::new(user_data), 
     } 
    } 

    pub fn lock(&self) -> FakeMutexGuard<T> { 
     FakeMutexGuard { 
      data: unsafe { &mut *self.data.get() }, 
     } 
    } 
} 

impl<'a, T> Deref for FakeMutexGuard<'a, T>{ 
    type Target = T; 
    fn deref<'b>(&'b self) -> &'b T { &*self.data } 
} 

impl<'a, T> DerefMut for FakeMutexGuard<'a, T>{ 
    fn deref_mut<'b>(&'b mut self) -> &'b mut T { &mut *self.data } 
} 

我的新代码是:

let shared_hash = Arc::new(FakeMutex::new(new_hash())); 

for _ in 0..n_cpu { 
    println!("start thread"); 
    let my_hash = shared_hash.clone(); 
    thread_pool.push(thread::spawn(move || { 
     let mut my_hash = my_hash.lock(); 
     let mut search_engine = SearchEngine::new(); 
     search_engine.search(&mut myhash); 
    })); 
} 

for i in thread_pool { 
    let _ = i.join(); 
} 

这解决了我的问题。

+0

我不认为这是一个好的解决方案。具体来说,你已经创建了一个可以用来包装任何其他类型**的类型来组合'Sync',但是你的'FakeMutex' *实际上并不安全*!请注意,Matthieu M.表示* **基础结构**已经是'Sync' *。你应该为你的哈希表实现'Sync',而不是疯狂地让任何类型奇迹般地变成'Sync'。 – Shepmaster

+0

我在回答这个问题,因为我不想让随机的人偶然发现它,并认为复制这种模式是个好主意,因为它是非常不安全的,也是最微妙的礼节。 – Shepmaster