2016-09-26 131 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