Skip to content

.NET Core Console.ReadLine() 在 Linux 中执行的奇怪问题

🏷️ .NET Core

Console.ReadLine() 在 Windows 中执行时会等待输入回车才会继续执行,但是发布到 Docker 上之后没有得到预想中的结果,程序运行到这里直接结束了。

示例代码如下:

csharp
static void Main(string[] args)
{
    Task.Run(() =>
    {
        // do somethind

        Thread.Sleep(Timeout.Infinite);
    });

    Console.WriteLine("Press [enter] to exit.");
    Console.ReadLine();

    Environment.Exit(-1);
}

不确定是因为什么原因导致的输入了回车主线程也没有退出 因为启动的线程中使用了 Thread.Sleep(Timeout.Infinite);,所以在程序的最后通过 Environment.Exit(-1); 来退出。
怀疑是因为新线程中创建了 RabbitMQ 的订阅导致的。(不明白为什么后台线程会阻止主线程的结束)

如果没有该行代码的话,在 windows 中即使输入了回车程序也不会退出。
怀疑是因为 Thread.Sleep(Timeout.Infinite); 导致的。虽然是在后台线程中执行的 Sleep,但是貌似会阻塞主线程的退出(这里不太确定,但从效果上来看是这样的)。

删除 Environment.Exit(-1); 之后在 Linux 上运行仍然直接退出了,然后就只能怀疑是 Console.ReadLine() 出错,从而导致主线程退出。可惜添加了 Try Catch 处理仍然没有打印出任何异常信息(这个以后有时间再做些测试)。

暂时的对策是在 Main 方法的最后使用 Thread.Sleep 来阻止主线程退出。

csharp
static void Main(string[] args)
{
    Task.Run(() =>
    {
        // do somethind

        Thread.Sleep(Timeout.Infinite);
    });

    Console.WriteLine("Close console to exit.");

    Thread.Sleep(Timeout.Infinite);
}

2019/6/27 追记

今天测试了一下 Console.ReadLine()Console.ReadKey()Console.Read()linux 中执行的效果。

测试环境是在 docker 中执行的,其 Dockerfile 的内容如下(基于 microsoft/dotnet:2.1-runtime 镜像):

docker
FROM microsoft/dotnet:2.1-runtime AS base
WORKDIR /app

FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY ReadLineTest17/ReadLineTest17.csproj ReadLineTest17/
RUN dotnet restore ReadLineTest17/ReadLineTest17.csproj
COPY . .
WORKDIR /src/ReadLineTest17
RUN dotnet build ReadLineTest17.csproj -c Release -o /app

FROM build AS publish
RUN dotnet publish ReadLineTest17.csproj -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "ReadLineTest17.dll"]

如下是测试代码及各自在 docker 中的执行结果。

csharp
static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
    Console.ReadLine();
    Console.WriteLine("Bye Bye !");
}

Console.ReadLine(); 没有报错,也没有任何效果。

csharp
static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
    Console.ReadKey();
    Console.WriteLine("Bye Bye 1 !");
}

Console.ReadKey(); 引发了异常:

Unhandled Exception: System.InvalidOperationException: Cannot read keys when either application does not have a console or when console input has been redirected. Try Console.Read.
at System.ConsolePal.ReadKey(Boolean intercept)
at ReadLineTest17.Program.Main(String[] args) in /src/ReadLineTest17/Program.cs:line 10

csharp
static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
    Console.Read();
    Console.WriteLine("Bye Bye 2 !");
}

根据上一个异常中的提示,改用了 Console.Read(); 方法,其和 Console.ReadLine(); 类似,没有任何效果。

csharp
static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
    Thread.Sleep(Timeout.Infinite);
    Console.WriteLine("Bye Bye 3 !");
}

这个是之前的对策,可以实现阻止主线程退出的效果。

csharp
static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
    Console.ReadLine();
    Console.ReadLine();
    Console.ReadLine();
    Console.ReadLine();
    Console.ReadLine();
    Console.ReadLine();
    Console.ReadLine();
    Console.ReadLine();
    Console.ReadLine();
    Console.ReadLine();
    Console.WriteLine("Bye Bye 4 !");
}

=.=||| 执行再多遍 Console.ReadLine(); 也没有任何效果。

测试了 C# Mono Console.ReadLine quitting 中的回答:

csharp
static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
    Console.WriteLine(readKeey());
    Console.WriteLine("Bye Bye 5 !");
}

private static string readKeey()
{
    ConsoleKeyInfo cki;
    Console.TreatControlCAsInput = true;

    string temp = "";
    do
    {
        cki = Console.ReadKey();
        temp = temp + cki.KeyChar;

    } while (cki.Key != ConsoleKey.Enter);
    return temp;
}

跟上面的 Console.ReadKey(); 同样的结果,引发了 System.InvalidOperationException 异常。