You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何多线程运行两个永不终止的函数?遇空引用异常求助

问题描述

我希望多线程运行两个永不终止的函数,具体代码如下:

输入监听函数

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 03:35:16