Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 如何防止线程在视觉上相互混淆';输出?_Multithreading_Io_Rust_Stdout_Println - Fatal编程技术网

Multithreading 如何防止线程在视觉上相互混淆';输出?

Multithreading 如何防止线程在视觉上相互混淆';输出?,multithreading,io,rust,stdout,println,Multithreading,Io,Rust,Stdout,Println,我有一个运行两个线程的程序,其中一个线程将状态消息打印到控制台,另一个线程接受用户输入。但是,因为它们都使用同一个控制台,如果我在打印另一个线程时使用一个线程在中途键入一个命令,那么它将获取我已经用它编写的内容(仅在视觉上-该命令仍将正确执行) 这是一个代码示例,如果您尝试在控制台中键入,它将不断受到第二个线程的干扰 使用std:{time,thread,io}; fn main(){ 线程::生成(移动| |{ 环路{ println!(“中断线”); 线程::睡眠(时间::持续时间::fro

我有一个运行两个线程的程序,其中一个线程将状态消息打印到控制台,另一个线程接受用户输入。但是,因为它们都使用同一个控制台,如果我在打印另一个线程时使用一个线程在中途键入一个命令,那么它将获取我已经用它编写的内容(仅在视觉上-该命令仍将正确执行)

这是一个代码示例,如果您尝试在控制台中键入,它将不断受到第二个线程的干扰

使用std:{time,thread,io};
fn main(){
线程::生成(移动| |{
环路{
println!(“中断线”);
线程::睡眠(时间::持续时间::from_millis(1000));
};
});
环路{
让mut userinput:String=String::new();
io::stdin().read_行(&mut userinput);
println!(“{}”,用户输入)
}
}
就目前的情况而言,当试图在控制台中键入“我正试图在这里写一个完整的句子”时,控制台最终会变成这样:

中断线路
我没有接错电话
我在试着打断电话
写一条破坏性的线
自动中断线
天丝中断线
我想在这里写一个完整的句子
中断线
中断线
正如您所看到的,当第二个线程循环并打印“中断行”时,我在控制台中写入的任何内容都会随该行一起进行。理想情况下,当我处于打字的中间时,它看起来就是这样(不管打字需要多长时间):

中断线路
中断线
中断线
我正在努力
然后,一旦我完成键入并按enter键,它将如下所示:

中断线路
中断线
中断线
我想在这里写一个完整的句子
我想在这里写一个完整的句子
其中第一句是我实际键入的输入,第二句是当它将我输入的内容打印回控制台时


有没有一种方法可以将行打印到控制台,而不会导致任何正在进行的用户输入被打印消息弄乱?

正如我们在上面的评论部分所提到的,您很可能希望使用外部库来处理每个终端的固有功能

然而,与上面讨论的不同,对于这样一个简单的“UI”,您甚至可能不需要
tui
,您可以使用(实际的板条箱
tui
在引擎盖下使用)

下面的代码片段正是您上面描述的,甚至还有一点点。但这只是一个粗略的初始实现,其中有许多东西需要进一步完善。(例如,您可能希望在程序运行时处理终端的调整大小事件,或者希望优雅地处理中毒的互斥状态等。)

因为下面的代码片段相当长,所以让我们把它分成小的、可消化的部分

首先,让我们从无聊的部分开始,所有的导入和一些我们将在整个代码中使用的类型别名

使用std::{
时间:持续时间,
线程::{
产卵,
睡觉
JoinHandle,
},
同步::{
弧,
互斥,
特里洛克罗,
原子::{
原子布,
订购,
},
},
io::{
自己
stdin,
斯特杜特,
写
},
};
使用术语::{
终端尺寸,
输入::TermRead,
清楚的
光标::Goto,
raw::IntoRawMode,
};
BgBuf型=电弧;
FgBuf型=电弧;
类型信号=电弧;
这是一种方式,我们可以集中在我们的背景线程。这是所有“中断”线路应该去的地方。(在此代码段中,如果按RETURN键,则键入的“command”也将添加到这些行中,以演示线程间通信。)

为了便于调试和演示,这些行被编入索引。由于后台线程实际上只是一个辅助线程,它不像处理用户输入的主线程(前台线程)那样具有攻击性,因此它只使用
try\u lock
。因此,最好使用线程本地缓冲区来存储在共享缓冲区不可用时无法放入其中的条目,这样我们就不会错过任何条目

fn bg_线程(bg_buf:BgBuf,
终止:信号)->JoinHandle
{
繁殖(移动)||
{
让mut i=0使用;
让mut local_buffer=Vec::new();
while!terminate.load(排序::松弛)
{
local_buffer.push(格式!(“[{}]中断行”,i));
匹配背景。尝试锁定()
{
正常(多缓冲区)=>
{
缓冲区。从\u片扩展\u(&本地\u缓冲区);
本地缓冲区。清除();
},
呃(TryLockError::中毒()))=>恐慌!(“BgBuf中毒”),
_ => (),
}
i+=1;
睡眠(持续时间:从_millis(1000));
};
})
}
然后是前台线程,它读取用户的输入。它必须在一个单独的线程中,因为它等待用户的按键(也称为事件),并且当它这样做时,它会阻塞它的线程

正如您可能注意到的,两个线程都使用
terminate
(一个共享的
AtomicBool
)作为信号。后台线程和主线程只读取它,而前台线程写入它。因为我们在前台线程中处理所有的键盘输入,自然这就是我们处理CTRL+C中断的地方,因此如果我们的用户想要退出,我们使用
terminate
向其他线程发出信号

fn fg_螺纹(fg_buf:FgBuf,
bg_buf:BgBuf,
终止:信号)->JoinHandle
{
使用termion::event::Key::*;
繁殖(移动)||
{
对于stdin()中的键。键()
{
匹配键。展开()
{