我正在尝试使用 Rust 编写一个国际象棋引擎,我希望它能够启动一个线程,以便它可以在思考时监听 uci 命令。这是我的代码的简化版本,它按预期工作,将停止标志和句柄传递给主线程,以便主线程可以停止 ai 线程,然后加入它。
#[test]
fn start_thread() {
let test_struct = TestStruct { stop_flag: None, test_data: String::from("not modified") };
println!("{}", test_struct.test_data);
let x = test_struct.start_thread();
thread::sleep(Duration::from_secs(1));
let test_struct = x.stop();
println!("{}", test_struct.test_data);
}
pub struct ThreadInfo {
stop_flag: Arc<AtomicBool>,
join_handle: JoinHandle<TestStruct>,
}
impl ThreadInfo {
pub fn stop(self) -> TestStruct {
println!("stopping");
self.stop_flag.store(true, Ordering::SeqCst);
println!("stop flag main thread: {}", self.stop_flag.load(Ordering::Relaxed));
let mut ai = self.join_handle.join().expect("failed to join thread");
ai.stop_flag = None;
println!("stopped");
ai
}
}
struct TestStruct {
stop_flag: Option<Arc<AtomicBool>>,
test_data: String,
}
impl TestStruct {
pub fn start_thread(mut self) -> ThreadInfo {
let stop_flag = Arc::new(AtomicBool::new(false));
let stop_flag_clone = Arc::clone(&stop_flag);
self.stop_flag = Some(stop_flag_clone);
println!("starting");
let handle = thread::spawn(move || {
println!("thread start");
loop {
println!("thread running");
//let start_time = Instant::now();
//let evaluation = self.negamax(&self.game.deep_clone(), depth, 0, -INFINITY, INFINITY);
//let pv_string = self.reconstruct_principal_variation();
if let Some(stop_flag) = &self.stop_flag {
if stop_flag.load(Ordering::Relaxed) { break; }
}
self.test_data = String::from("modified");
//let depth_duration = start_time.elapsed();
//Self::uci_print_evaluation_info(evaluation, depth, depth_duration, pv_string);
//self.uninterupted_best_move = Some(self.last_principal_variation[0]);
}
self
});
println!("started {:?}", handle);
ThreadInfo {
stop_flag: stop_flag,
join_handle: handle,
}
}
}
问题是,这是我的引擎中实际代码的简化版本,并且不起作用。我惊慌失措!在线程的第一行声明,只是为了看看它是否正在运行线程内部的代码,并且它不会panic。当我对简化代码执行此操作时,它确实出现恐慌并产生错误消息。停止标志实际上并没有停止线程,并且它永远不会加入。我看不出我的代码与上面有效的测试代码有何不同。以下是我的代码的相关部分:
pub struct ThreadInfo {
stop_flag: Arc<AtomicBool>,
join_handle: JoinHandle<AI>,
}
impl ThreadInfo {
pub fn stop(self) -> AI {
println!("stopping");
self.stop_flag.store(true, Ordering::SeqCst);
println!("stop flag main thread: {}", self.stop_flag.load(Ordering::Relaxed));
let mut ai = self.join_handle.join().expect("failed to join thread");
ai.stop_flag = None;
println!("stopped");
ai
}
}
pub struct AI {
move_data: AllMoveData,
hashes: Hashes,
game: Game,
name: String,
transposition_table: TranspositionTable,
last_principal_variation: Vec<EncodedMove>,
stop_flag: Option<Arc<AtomicBool>>,
uninterupted_best_move: Option<EncodedMove>,
}
impl AI {
pub fn start_thread(mut self) -> ThreadInfo {
let stop_flag = Arc::new(AtomicBool::new(false));
let stop_flag_clone = Arc::clone(&stop_flag);
self.stop_flag = Some(stop_flag_clone);
println!("starting");
let handle = thread::spawn(move || {
println!("thread start");
panic!("thread started! yay!");
for depth in 1..MAX_DEPTH {
let start_time = Instant::now();
let evaluation = self.negamax(&self.game.deep_clone(), depth, 0, -INFINITY, INFINITY);
let pv_string = self.reconstruct_principal_variation();
if self.check_stop_flag() { break; }
let depth_duration = start_time.elapsed();
Self::uci_print_evaluation_info(evaluation, depth, depth_duration, pv_string);
self.uninterupted_best_move = Some(self.last_principal_variation[0]);
}
self
});
println!("started {:?}", handle);
ThreadInfo {
stop_flag: stop_flag,
join_handle: handle,
}
}
fn check_stop_flag(&self) -> bool {
if let Some(stop_flag) = &self.stop_flag {
return stop_flag.load(Ordering::Relaxed);
}
false
}
}
这是 main.rs 中调用此代码的部分:
let ai_thread = ai.start_thread();
thread::sleep(Duration::from_millis(move_time));
ai = ai_thread.stop();
writeln!(handle, "bestmove {}", ai.get_best_move()).unwrap();
negamax 和 quiescent 函数在函数开始处和循环内调用 check_stop_flag,因此它只返回 0 并使用上次完成的搜索的结果。当我以 1000 毫秒运行 main 函数时,它确实在打印“开始”和“停止”之间等待一秒钟,但它永远不会“停止”,也永远不会打印移动。它似乎甚至没有尝试运行线程内的任何代码。是什么让我的代码不像简化版本那样表现?
原来问题是println!声明。它们在测试函数的线程中工作得很好,但在 ai 线程中放置一个 printline 函数,它就会冻结。我的引擎在搜索时不再打印信息,但它现在可以运行并且可以提供上次搜索的信息。