如何等待不期望 return 的异步方法

How to await an async method which isn't expected to return

我正在制作一个 TCP 服务器。我正在使用 async/await 来处理多线程。我用于侦听传入客户端的方法以及后续消息看起来有点像这样:

private static async Task Listener()
        {
            while (Online)
            {
                TcpClient socket = await tcpListener.AcceptTcpClientAsync();
                OnReceivedTcpClient(socket);
            }           
        }

如您所知,此方法预计不会很快 return。我的问题是关于我应该如何调用这个侦听器方法。目前我这样做的方式是这样的:

(控制台应用程序) 在 Main 中的 Program.cs 中,我调用 Server.Start()

static void Main(string[] args)
        {
            Console.Title = "Server (Prototype)";

            Server.Start(100, 26950);

            ConsoleKeyInfo input;
            do
            {
                input = Console.ReadKey();

                // check input and do stuff
                }
        }
            while (input.Key != ConsoleKey.C);  
            
        }

Server.Start 初始化一些值,然后调用一个事件,该事件又调用监听器

private static event EventHandler<EventArgs> StartEvent;
private static void OnStartEvent() => StartEvent?.Invoke(null, new EventArgs());

public static void Start(int maxClients, int port)
        {
            Stop();
            Console.WriteLine("Starting server...");
            Init(maxClients, port);
            OnStartEvent();
        }

private async void ServerOnStartEvent(object sender, EventArgs e)
        {           
            Online = true;          
            Console.WriteLine($"Server started on port {Port}");
            await Listener();
        }

如果我调用了 await Listener();在 Server.Start 内部,那么该方法将需要 async 关键字,并且它必须 return void (我知道这不是一个理想的设计)或 return 一个任务,这意味着我必须在 program.cs 中调用 _ = Server.Start() (这也不是很好的设计)。

所以我的问题是,我的解决方案是等待异步任务方法的好方法吗?还有更好的方法吗?

我通常的处理方式是同时添加一个Stop-method。因此 Start 启动任务并将其保存在一个字段中。 stop 方法请求任务停止(通过任何方式),并且 returns 存储的任务。

因此调用者可以等待 stop 方法的结果,一旦任务完成,调用者可以确定所有资源都已清理等。

一个变体是让 Start 方法 return 类似于 IAsyncDisposable,它可以允许 using 语句在超出范围时自动停止并等待清理。

示例:

public class MyClass
    volatile bool stopRequested; // or use CancellationTokenSource
    Task task;
    public void Start() => task = Task.Run(DoWork); // Should probably make this "longRunning"
    public void DoWork(){
        while(!stopRequested){
            // Do something that take a limited amount of time.
        }
        // Do cleanup
    }
    public Task Stop(){
        stopRequested = true;
        return task;
    }