C#控制台程序首次输出后内容冻结,无法实现同位置覆盖输出怎么办?
问题原因
- 核心是类Unix系统(Linux/macOS)的控制台API不是线程安全的:你的
Sequence.OnNext是在Task.Run创建的后台线程中执行,Subscribe的回调会直接在这个后台线程调用Console相关方法;同时主线程正在执行Console.ReadKey监听按键,两个线程同时竞争控制台资源时会触发死锁,直接导致程序冻结。 - 额外逻辑问题:你当前的输出逻辑顺序错误,先执行
Console.WriteLine输出值再回退光标清行,多线程下容易出现光标位置计算错误,进一步加剧异常概率。
修复方案
两步即可解决:
- 对所有控制台操作加独占锁,避免多线程同时访问控制台资源
- 简化同位置覆盖逻辑,直接用
\r回退到行首后输出新内容,不需要额外操作光标位置;如果输出内容长度不固定,可调用PadRight补全整行避免残留旧字符。
修正后可运行代码
using System; using System.Threading; using System.Threading.Tasks; using System.Reactive.Subjects; namespace ReactiveProgramming { class Program { // 全局控制台独占锁 private static readonly object _consoleLock = new object(); static void Main(string[] args) { var generateSeq = new GenerateSequence(); lock(_consoleLock) { Console.WriteLine("Hello World!"); } generateSeq.Sequence.Subscribe(val => { lock(_consoleLock) { // 回行首后输出新值,补空格覆盖旧内容,也可替换为PadRight(Console.BufferWidth)适配动态长度 Console.Write($"\r{val} "); } }); generateSeq.Run(); } } class GenerateSequence { public Subject<int> Sequence = new Subject<int>(); public void Run(int runTimes = 10) { ConsoleKeyInfo cki; Task.Run(() => runForTimes(runTimes)); do { // 读取按键操作也要加锁保证线程安全,true参数避免按下的按键回显到控制台 lock(Program._consoleLock) { cki = Console.ReadKey(true); } } while (cki.Key != ConsoleKey.Escape); } public void runForTimes(int runTimes = 10) { for (var i = 0; i < runTimes; i++) { Sequence.OnNext(i); Thread.Sleep(1000); } // 输出完成后换行,避免后续终端输入和最后一个输出值重叠 lock(Program._consoleLock) { Console.WriteLine(); } } } }
内容的提问来源于stack exchange,提问作者microchip78
相关产品推荐
相关产品推荐

