当所有进程都在 c# 的单独线程上运行时,如何保持控制台应用程序打开?

Posted

技术标签:

【中文标题】当所有进程都在 c# 的单独线程上运行时,如何保持控制台应用程序打开?【英文标题】:How can I keep a Console App open when all processes are running on separate threads in c#? 【发布时间】:2019-04-24 08:52:33 【问题描述】:

我正在尝试制作一个简单的服务器应用程序来学习 C# 中多线程服务器编程的基础知识。基本思想很简单:客户端连接到服务器并发送:“get time”来接收服务器的当前时间。所有 tcplistener 线程和套接字都应该在单独的线程上运行。我不知道为什么,但是当应用程序完成所有线程的初始化时,控制台应用程序就会关闭。

这是服务器代码:

class Program

    static public int minPort;
    static public int maxPort;
    static int openPort = 0;
    static byte[] sendData = new byte[1024];
    static TcpListener[] listeners;
    static Thread[] connectionThreads;

    static void Main()
    
        Console.Write("What do you want your minimum port to be? ");
        minPort = Convert.ToInt32(Console.ReadLine());
        Console.Write("What do you want your maximum port to be? ");
        maxPort = Convert.ToInt32(Console.ReadLine());

        //init
        ThreadStart streamThreadStart = new ThreadStart(DataStream);
        openPort = maxPort - minPort;
        listeners = new TcpListener[maxPort - minPort];
        connectionThreads = new Thread[maxPort - minPort];

        for (int i = 0; i == maxPort - minPort; i++)
        
            listeners[i] = new TcpListener(IPAddress.Any, minPort + i);
            connectionThreads[i] = new Thread(streamThreadStart);
            Thread.Sleep(10);
            openPort = openPort + 1;
        
    

    static void DataStream()
    
        int port = openPort;
        byte[] receiveData = new byte[1024];
        listeners[openPort].Start();
        Socket socket = listeners[port].AcceptSocket();
        NetworkStream stream = new NetworkStream(socket);
        while (true)
        
            socket.Receive(receiveData);
            Console.WriteLine("Received: " + BitConverter.ToString(receiveData));
            socket.Send(parseCommand(receiveData));
        

    
    static byte[] parseCommand(byte[] input)
    
        string RCommand = BitConverter.ToString(input);
        string SCommand;
        if (RCommand == "get time")
        
            SCommand = DateTime.Now.ToUniversalTime().ToString();
        else
        
            SCommand = "Unknown Command, please try again";
        
        byte[] output = Encoding.ASCII.GetBytes(SCommand);
        return output;
    

【问题讨论】:

在控制台应用程序中,当 main 完成时,应用程序将关闭,因为没有更多代码可以执行。您需要在 main 中构建一个循环以保持控制台应用程序运行。 您知道一个侦听器套接字可以服务多个客户端并移交接受的套接字吗?除非您正在做一些非常奇怪的事情,否则永远不需要在多个端口上运行侦听器。例如。普通的 Web 服务器监听端口 80 和/或 443。你认为这些处理数千个客户端的服务器一次只处理一个客户端吗?这里需要大量的研究/阅读。 另外,您必须注意来自Receive 的返回值和道德等价物。因为 TCP 给你的只是一个字节流,而不是消息。它不保证会填满您的缓冲区,或者您将收到的内容与传递给另一端 Send 的单个调用的单个缓冲区匹配。 另一种选择是创建一个Windows Service,它旨在长期运行而不关闭。 【参考方案1】:

您需要在退出之前join您的线程。

static public void Main()

    /*
        Existing code goes here
    */

    //Before exiting, make sure all child threads are finished
    foreach (var thread in connectionThreads) thread.Join(); 

当你的程序调用 Thread.Join 时,它告诉操作系统它不需要为任何时间片调度主线程,直到子线程完成。这使得它比其他技术更轻,例如busywaiting(即运行while循环);虽然主线程仍然会占用资源,但它不会消耗任何 CPU 时间。

另见When would I use Thread.Join?和c# Waiting for multiple threads to finish

【讨论】:

虽然我喜欢这个答案,因为它讨论了将线程连接回主线程的正确方法。它不处理无法控制的控制台应用程序的问题。解决方案是,如 Windows 服务所建议的那样,或更改主线程以便它也对用户输入做出反应。【参考方案2】:

一般来说,最佳做法是让控制台应用程序在用户想要停止应用程序时提供“输入任意键退出”提示。但是您始终可以查询要退出的特定按键,例如“q”。

这里有一些代码可以帮助您入门:

Console.WriteLine("Press any key to exit..."); Console.ReadKey();

【讨论】:

【参考方案3】:

您的代码没有启动新线程! 因此,我对您的代码进行了一些更改:

class Program
    
        static public int minPort;
        static public int maxPort;
        //static int openPort = 0;
        static byte[] sendData = new byte[1024];
        static TcpListener[] listeners;
        static Thread[] connectionThreads;

        static void Main()
        
            Console.Write("What do you want your minimum port to be? ");
            minPort = Convert.ToInt32(Console.ReadLine());
            Console.Write("What do you want your maximum port to be? ");
            maxPort = Convert.ToInt32(Console.ReadLine());

            //init
           // ThreadStart streamThreadStart = new ThreadStart(DataStream(0));
            //openPort = minPort;
            listeners = new TcpListener[maxPort - minPort];
            connectionThreads = new Thread[maxPort - minPort];

            //for (int i = 0; i == maxPort - minPort; i++) it can't work
            for (int i = 0; i < maxPort - minPort; i++)
            
                listeners[i] = new TcpListener(IPAddress.Any, minPort + i);
                connectionThreads[i] = new Thread(new ParameterizedThreadStart(DataStream)); //thread with start parameter
                connectionThreads[i].Start(i); // start thread with index
                Thread.Sleep(10);

            

        

        static void DataStream(object o)
        
            int index = (int)o; //get index
            byte[] receiveData = new byte[1024];
            listeners[index].Start(); // start listener with right index
            Socket socket = listeners[index].AcceptSocket();
            NetworkStream stream = new NetworkStream(socket);
            while (true)
            
                socket.Receive(receiveData);
                Console.WriteLine("Received: " + BitConverter.ToString(receiveData));
                socket.Send(parseCommand(receiveData));
            

        
        static byte[] parseCommand(byte[] input)
        
            string RCommand = BitConverter.ToString(input);
            string SCommand;
            if (RCommand == "get time")
            
                SCommand = DateTime.Now.ToUniversalTime().ToString();
            
            else
            
                SCommand = "Unknown Command, please try again";
            
            byte[] output = Encoding.ASCII.GetBytes(SCommand);
            return output;
        
    

【讨论】:

请详细描述您所做的更改以及为什么它会成为答案。

以上是关于当所有进程都在 c# 的单独线程上运行时,如何保持控制台应用程序打开?的主要内容,如果未能解决你的问题,请参考以下文章

c# 一个程序关闭,如果有前台线程还在运行,当前台线程运行完是不是会关闭?还是一直存在?

C# 计时器是不是在单独的线程上运行?

如何在并行 JVM 进程(不是线程)中运行 TestNG 测试

linux服务器CPU占用率800%左右...

C# 中 UI 线程和子进程之间的低延迟 IPC [关闭]

C#多线程和线程池