2016-09-26 4 views
0

ich die Portierung meiner C++ Schach-Engine in Rust. Ich habe eine große Hash-Tabelle zwischen Such-Threads geteilt und in der C++ - Version ist diese Tabelle gesperrt; Es gibt keinen Mutex für das Teilen des Lese-/Schreibzugriffs. Hier ist the theory, wenn Sie interessiert sind.Teilen Lock-weniger Ressourcen zwischen Threads in Rust

Im Rust Version dieses Codes ist es gut funktioniert, nutzt aber ein 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(); 
} 

Wie kann ich den Tisch zwischen Threads ohne Mutex teilen?

+2

Ich bin nicht sicher, dass es eine angemessene Antwort darauf gibt. Wenn eine Datenstruktur nicht gesperrt werden muss, benötigen Sie keinen Mutex. Wenn dies der Fall ist, brauchen Sie * einen * Mutex, und der Versuch, die Verwendung eines solchen zu vermeiden, ist unsicher. Ich weiß nicht, ob es eine bereits existierende, anwendbare Lösung gibt, weil Sie nicht angegeben haben, was Sie versuchen, außer "kein Mutex". Und wenn nicht, dann geht das in "empfehlen Sie eine Bibliothek" (die möglicherweise geschlossen wird, um zu breit zu sein), oder "wie schreibe ich eine lock-free Datenstruktur" (die * auch * wahrscheinlich zu breit ist) . –

+0

Auch durch diesen Link Spiegeln (ich habe keine Zeit, einen ganzen Artikel zu lesen, eine etwas vage Frage zu versuchen und zu beantworten), frage ich mich, warum Sie nicht nur 'Handy ' verwenden können. Aber ich weiß nicht, was 'new_hash()' tut, also \ * shrug \ * –

+2

Wie Matthieu M. antwortet, müssen Sie Ihre Datenstruktur implementieren 'Sync' implementieren, was anzeigt, dass sie sicher zwischen Threads geteilt werden kann. Das ändert das Problem auf Ihre eigentliche Frage: "Wie schreibe ich diese ** besondere ** blocklose Datenstruktur in Rust?". Um das zu tun, werden Sie * wahrscheinlich * unsicheren Code schreiben müssen. Es scheint, dass die fragliche Hashtabelle bestimmte ** Hardware ** -Voraussetzungen erfordert (z. B. 64-Bit-Speicher im Speicher sind natürlich atomar) und ** Software ** -Voraussetzungen (z. B. speichern Sie zwei 64-Bit-Werte). Das scheint anzuzeigen, dass die Datenstruktur nicht tragbar ist. – Shepmaster

Antwort

4

Ganz einfach, eigentlich: die Mutex ist unnötig, wenn die zugrunde liegende Struktur bereits Sync ist.

In Ihrem Fall wäre ein Array von Strukturen von atomics zum Beispiel arbeiten. Sie können Rusts verfügbare Atomics here finden.

-1

Wie bereits angedeutet, habe ich eine FakeMutex schrieb die Sync implementiert, aber nicht gesperrt wirklich die Hash-Tabelle:

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 } 
} 

Mein neuer Code ist:

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(); 
} 

Diese meine Frage löst.

+0

Ich denke nicht, dass dies eine gute Lösung ist. Speziell haben Sie einen Typ erstellt, mit dem ** ein beliebiger anderer Typ ** umschlossen werden kann, um die Kombination 'Sync' zu erstellen, aber Ihr' FakeMutex' * macht es nicht wirklich *! Beachten Sie, dass Matthieu M. sagte * die ** zugrunde liegende Struktur ** ist bereits 'Sync' *.Sie sollten 'Sync' für Ihre Hash-Tabelle implementieren und nicht zulassen, dass irgendein Typ magisch zu 'Sync' wird. – Shepmaster

+0

Ich verurteile diese Antwort, weil ich nicht will, dass zufällige Leute darüber stolpern und denke, dass es eine gute Idee ist, dieses Muster zu kopieren, da es extrem unsicher und in den subtilsten Manieren ist. – Shepmaster

0

Data races are undefined behavior sowohl in C++ und Rost. Sprich: Nein, nur

Der richtige Weg ist Ihre Tabelle atomic integers aus zu bauen. Es ist Raketenwissenschaft. You have to decide case by case wie viel Sie care about the order of memory operations. Das macht Ihren Code überflutet:

// non-atomic array access 
table[h] = 0; 

// atomic array access 
table[h].store(0, Ordering::SeqCst); 

Aber es ist es wert.

nicht sagen, Es ist, was die Leistungseinbuße sein - man muss es einfach ausprobieren.

Verwandte Themen