- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个运行两个线程的程序,其中一个将状态消息打印到控制台,另一个接受用户输入。但是,因为它们都使用相同的控制台,如果我在另一个线程打印时使用一个线程中途键入命令,它将采用我已经用它编写的内容(仅在视觉上 - 命令仍将正确执行)。
这是一个代码示例,如果您尝试在控制台中键入内容,它将不断受到第二个线程的干扰。
use std::{time,thread,io};
fn main() {
thread::spawn(move || {
loop {
println!("Interrupting line");
thread::sleep(time::Duration::from_millis(1000));
};
});
loop {
let mut userinput: String = String::new();
io::stdin().read_line(&mut userinput);
println!("{}",userinput)
}
}
现在,当尝试在控制台中输入“我正在尝试在这里写一个完整的句子”时,控制台最终看起来是这样的:
Interrupting line
i aInterrupting line
m trying Interrupting line
to write a fInterrupting line
ull senInterrupting line
tence hereInterrupting line
i am trying to write a full sentence here
Interrupting line
Interrupting line
如您所见,当第二个线程循环并打印“Interrupting line”时,我在控制台中写入的任何内容都与该行一起传送。理想情况下,当我正在打字时,它看起来像这样(无论打字需要多长时间):
Interrupting line
Interrupting line
Interrupting line
i am trying to
然后,当我完成输入并按下回车键后,它会看起来像这样:
Interrupting line
Interrupting line
Interrupting line
i am trying to write a full sentence here
i am trying to write a full sentence here
第一句是我实际输入的内容,第二句是将我输入的内容打印回控制台。
有没有一种方法可以将行打印到控制台,而不会导致任何正在进行的用户输入被打印消息破坏?
最佳答案
正如我们在上面的评论部分中提到的,您很可能希望使用外部库来处理每个终端的内在特性。
但是,与上面讨论的不同,对于这样一个简单的“UI”,您甚至可能不需要 tui
,您可以使用 termion
来摆脱困境(实际的 crate tui
在后台使用)。
以下代码片段完全按照您上面的描述进行操作,甚至还多了一点点。但这只是一个粗略的初步实现,里面还有很多东西需要进一步完善。 (例如,您可能希望在程序运行时处理终端的调整大小事件,或者您希望优雅地处理中毒的互斥锁状态等)
因为下面的代码片段很长,所以让我们分成小的、易于理解的 block 来过一遍。
首先,让我们从无聊的部分开始,我们将在整个代码中使用所有导入和一些类型别名。
use std::{
time::Duration,
thread::{
spawn,
sleep,
JoinHandle,
},
sync::{
Arc,
Mutex,
TryLockError,
atomic::{
AtomicBool,
Ordering,
},
},
io::{
self,
stdin,
stdout,
Write,
},
};
use termion::{
terminal_size,
input::TermRead,
clear,
cursor::Goto,
raw::IntoRawMode,
};
type BgBuf = Arc<Mutex<Vec<String>>>;
type FgBuf = Arc<Mutex<String>>;
type Signal = Arc<AtomicBool>;
这不碍事,我们可以专注于我们的后台线程。这是所有“中断”线应该去的地方。 (在此代码段中,如果您按 RETURN,则输入的“命令”也将添加到这些行中,以演示线程间通信。)
为了便于调试和演示,对行进行了索引。由于后台线程实际上只是一个辅助线程,它不像处理用户输入的主要线程(前台线程 ) 所以它只使用 try_lock
。因此,最好使用线程本地缓冲区来存储在共享缓冲区不可用时无法放入共享缓冲区的条目,这样我们就不会错过任何条目。
fn bg_thread(bg_buf: BgBuf,
terminate: Signal) -> JoinHandle<()>
{
spawn(move ||
{
let mut i = 0usize;
let mut local_buffer = Vec::new();
while !terminate.load(Ordering::Relaxed)
{
local_buffer.push(format!("[{}] Interrupting line", i));
match bg_buf.try_lock()
{
Ok(mut buffer) =>
{
buffer.extend_from_slice(&local_buffer);
local_buffer.clear();
},
Err(TryLockError::Poisoned(_)) => panic!("BgBuf is poisoned"),
_ => (),
}
i += 1;
sleep(Duration::from_millis(1000));
};
})
}
然后是我们的前台线程,它读取用户的输入。它必须在一个单独的线程中,因为它等待来自用户的按键(又名事件),并且在这样做时它会阻塞它的线程。
正如您可能注意到的那样,两个线程都使用 terminate
(共享的 AtomicBool
)作为信号。后台线程和主线程只读它,而这个前台线程写它。因为我们在前台线程中处理所有键盘输入,自然这是我们处理 CTRL + C 中断的地方,因此我们使用 terminate
如果我们的用户想要退出,则向其他线程发出信号。
fn fg_thread(fg_buf: FgBuf,
bg_buf: BgBuf,
terminate: Signal) -> JoinHandle<()>
{
use termion::event::Key::*;
spawn(move ||
{
for key in stdin().keys()
{
match key.unwrap()
{
Ctrl('c') => break,
Backspace =>
{
fg_buf.lock().expect("FgBuf is poisoned").pop();
},
Char('\n') =>
{
let mut buf = fg_buf.lock().expect("FgBuf is poisoned");
bg_buf.lock().expect("BgBuf is poisoned").push(buf.clone());
buf.clear();
},
Char(c) => fg_buf.lock().expect("FgBuf is poisoned").push(c),
_ => continue,
};
}
terminate.store(true, Ordering::Relaxed);
})
}
最后但并非最不重要的一点是我们的主线程。我们在这里创建了三个线程共享的主要数据结构。我们将终端设置为“原始”模式,这样我们就可以手动控制屏幕上显示的内容,而不是依赖于某些内部缓冲,因此我们可以实现剪辑机制。
我们测量终端窗口的大小以确定我们应该从背景缓冲区中打印出多少行。
在每个成功的帧渲染之前,我们清除屏幕,然后打印出背景缓冲区的最后 n 个条目,然后将用户输入打印为最后一行。然后为了最终让这些东西出现在屏幕上,我们刷新了 stdout
。
如果我们收到终止信号,我们会清理其他两个线程(即等待它们完成)、清除屏幕、重置光标位置,然后向我们的用户说再见。
fn main() -> io::Result<()>
{
let bg_buf = Arc::new(Mutex::new(Vec::new()));
let fg_buf = Arc::new(Mutex::new(String::new()));
let terminate = Arc::new(AtomicBool::new(false));
let background = bg_thread(Arc::clone(&bg_buf),
Arc::clone(&terminate));
let foreground = fg_thread(Arc::clone(&fg_buf),
Arc::clone(&bg_buf),
Arc::clone(&terminate));
let mut stdout = stdout().into_raw_mode().unwrap();
let (_, height) = terminal_size().unwrap();
while !terminate.load(Ordering::Relaxed)
{
write!(stdout, "{}", clear::All)?;
{
let entries = bg_buf.lock().expect("BgBuf is poisoned");
let entries = entries.iter().rev().take(height as usize - 1);
for (i, entry) in entries.enumerate()
{
write!(stdout, "{}{}", Goto(1, height - i as u16 - 1), entry)?;
}
}
{
let command = fg_buf.lock().expect("FgBuf is poisoned");
write!(stdout, "{}{}", Goto(1, height), command)?;
}
stdout.flush().unwrap();
sleep(Duration::from_millis(50));
}
background.join().unwrap();
foreground.join().unwrap();
writeln!(stdout, "{0}{1}That's all folks!{1}", clear::All, Goto(1, 1))
}
如果我们将所有这些东西放在一起,编译并运行它,我们可以获得以下输出:
[0] Interrupting line
[1] Interrupting line
[2] Interrupting line
[3] Interrupting line
This is one command..
[4] Interrupting line
[5] Interrupting line
..and here's another..
[6] Interrupting line
[7] Interrupting line
..and it can do even more!
[8] Interrupting line
[9] Interrupting line
Pretty cool, eh?
[10] Interrupting line
[11] Interrupting line
[12] Interrupting line
[13] Interrupting line
I think it is! :)
关于multithreading - 如何防止线程在视觉上混淆彼此的输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57520623/
我真的很困惑。我已经尝试使用带有 tomcat 的 Jax-rs 并使用所有注释,我能够使用 url 调用我的服务。因此,如果没有 Jax-rs,我可以简单地拥有一个 servlet 并调用我的服务。
是否有任何工具/商业混淆器可以混淆 WPF 控件中的 BAML 资源? 如果没有,就 IP 保护而言,这是一段艰难的时期,因为黑客可以通过使用 BAML 到 XAML 转换器轻松查看 BAML 资源。
嘿大家。我在尝试使用 COBOL 在 zOS 环境中解决的编码项目中遇到了一些麻烦。我需要读入一个文件并将它们放入索引表中(我知道将少于 90 条记录)。 让我感到困扰的是,我们受到项目参数的约束,以
我试图按照这个例子来理解 join() 方法: class PrintDemo { public void printCount() { try { for(int
当我编译我正在编写的代码,然后在 JD Gui 中查看时,方法显示带有如下标题: public void growSurface(Random paramRandom, int paramInt1,
我正在为重新分发准备 Android 库,它的代码必须进行混淆处理。我已经阅读了有关此主题的一些内容,并且决定使用 Android Library Project。它将作为 jar 分发(自动在/bi
两个混淆相关的问题: 1) 是否有任何工具可以将 F# 从 MSIL 目标形式反汇编回其源形式或接近它的形式?这不是通过默默无闻来实现安全性的尝试,但我想保护某些源代码免遭“盗窃”。 2) 我简要地查
谁能向我解释为什么 simulatedCase <- rbinom(100,1,0.5) simDf <- data.frame(CASE = simulatedCase) posterior_m0
我一直无法找到关于使用 AppDomains 时发生的事情的非常清楚的描述,所以希望有人能够启发我。我有一个简单的测试程序(基本上是扯掉了 MSDN example ): using System;
假设我有 2 个分支topic和 master如果我在 topic分支,然后运行 git rebase master它是 rebase master 还是 rebase 主题分支? 做 git r
我有一个类(class): public class LockTest { public void LockThis() { lock (this)
我正在尝试最小化/混淆我的 Angular 代码,但遇到了问题。我在这里阅读“缩小说明”http://docs.angularjs.org/tutorial/step_05但我定义我的 Control
我遇到了一些困惑的操作。 var a = 0.1; var b = 0.2; var c = 0.3; console.log(a); // 0.1 console.log(b); // 0.2 co
感谢您查看我的帖子 - 我正在尝试弄清楚如何在单击链接时关闭此下拉菜单,但我的 JavaScript 技能非常缺乏,而且代码似乎很困惑。这是 HTML:
混淆、哈希和加密之间有什么区别? 这是我的理解: 哈希是一种单向算法;无法逆转 混淆与加密类似,但不需要任何“ secret ”即可理解(ROT13 就是一个例子) 加密是可逆的,但需要“ secre
我有以下代码 my $content = $response->content; $content =~ /username=([\s\S]+?)&/; my $username = $1; prin
我在 .NET 中发现了一些与我预期的有点不同的东西。我粘贴的代码没有意义,但它是我拥有的一个复杂得多的函数的浓缩版。我实际上是在获取匿名类型信息作为参数(尚未创建匿名类型的实例),我需要创建该类型的
我正在努力解决 JavaFX 应用程序的混淆问题。使用此项目作为基础: https://github.com/openjfx/samples/tree/master/IDE/IntelliJ/Non-
是否可以制作一个与此类似的 CSV 阅读器 while((line = reader.readLine()) != null){ String[] values = line.
公共(public)类测试2 { 公共(public)静态无效主(字符串[]参数){ System.out.println("3 + 6"); System.out.println(3
我是一名优秀的程序员,十分优秀!