如何多线程运行两个永不终止的函数?遇空引用异常求助
问题描述
我希望多线程运行两个永不终止的函数,具体代码如下:
输入监听函数
public static void playerInput() { while (true) { ConsoleKeyInfo pressedKey = Console.ReadKey(); Doors.input = pressedKey.KeyChar; Console.Write('\b'); } }
门操作函数
public static void openingDoor() { while (true) { if (Doors.input == 'o') { openDoor(); Console.WriteLine($"You're now at door {door}"); input = ' '; } } }
其中openDoor()是仅将某个值递增1的函数。
尝试的多线程实现
Thread pInput = new Thread(playerInput); Thread doorOpen = new Thread(openingDoor); pInput.Start(); doorOpen.Start(); pInput.Join(); doorOpen.Join();
运行初期正常,但按下约6次'o'后,程序崩溃并抛出如下NullReferenceException:
System.NullReferenceException: Object reference not set to an instance of an object at System.Timers.Timer.UpdateTimer() at System.Timers.Timer.set_Enabled(Boolean value) at CSharpShellCore.IDE.Views.ConsoleView.QueueWriteLine(String text) at System.Object.CallSite.Target(Closure , CallSite , Object , String ) at CSharpShellCore.Console.WriteLineToView(Object o) at CSharpShellCore.Console.WriteLine(String n) at Doors.Doors.openingDoor() at System.Threading.Thread.StartCallback() *** **** ***System.NullReferenceException: Object reference not set to an instance of an object at System.Timers.Timer.UpdateTimer() at System.Timers.Timer.set_Enabled(Boolean value) at CSharpShellCore.IDE.Views.ConsoleView.QueueWriteLine(String text) at System.Object.CallSite.Target(Closure, CallSite, Object, String) at CSharpShellCore.Console.WriteLineToView(Object o) at CSharpShellCore.Console.WriteLine(String n) at Doors.Doors.openingDoor() at System.Threading.Thread.StartCallback()
解决方案
1. 修复共享变量的线程安全问题
Doors.input是多线程共享变量,当前无同步机制会引发竞态条件,导致数据不一致或异常。可以通过两种方式解决:
- 方式一:使用
volatile关键字(适合简单的读写场景)
修改共享变量的定义:public static volatile char input = ' '; - 方式二:使用
lock同步访问(更可靠的同步方式)
先定义一个全局锁对象,再在所有访问input的地方加锁:// 定义锁对象,全局唯一 private static readonly object _syncLock = new object(); // 修改playerInput中的赋值逻辑 public static void playerInput() { while (true) { ConsoleKeyInfo pressedKey = Console.ReadKey(); lock(_syncLock) { Doors.input = pressedKey.KeyChar; } Console.Write('\b'); } } // 修改openingDoor中的判断与赋值逻辑 public static void openingDoor() { while (true) { lock(_syncLock) { if (Doors.input == 'o') { openDoor(); Console.WriteLine($"You're now at door {door}"); Doors.input = ' '; } } } }
2. 解决控制台操作的线程冲突
多个线程同时调用Console.ReadKey和Console.WriteLine会导致控制台内部状态混乱,这是触发CSharpShellCore空引用异常的核心原因。需要给控制台操作也加锁:
// 修改playerInput,把控制台操作也纳入锁范围内 public static void playerInput() { while (true) { lock(_syncLock) { ConsoleKeyInfo pressedKey = Console.ReadKey(); Doors.input = pressedKey.KeyChar; Console.Write('\b'); } } } // 修改openingDoor中的控制台输出 public static void openingDoor() { while (true) { lock(_syncLock) { if (Doors.input == 'o') { openDoor(); Console.WriteLine($"You're now at door {door}"); Doors.input = ' '; } } Thread.Sleep(10); // 加入短暂休眠,降低CPU空转消耗 } }
3. 优化循环减少CPU占用
openingDoor中的无限循环会持续占用CPU资源,加入Thread.Sleep(10)可以让线程短暂休眠,大幅降低CPU使用率,同时不影响功能响应速度。
内容的提问来源于stack exchange,提问作者egg
相关产品推荐
相关产品推荐

