CancellationTokenSource是否正确关闭

时间:2016-05-01 21:51:20

标签: c#

以下是切入点:

public class Program
{
    private static readonly CancellationTokenSource TokenSource = new CancellationTokenSource();

    public static void Main(string[] args)
    {
        // start the app
        new Bootstrap()
            .RunAsync(TokenSource.Token)
            .Wait();

        Console.CancelKeyPress += (sender, eventArgs) =>
        {
            TokenSource.CancelAfter(500);
        };
    }
}

这是引导程序:

public class Bootstrap : IBootstrap
{
    private readonly IServer server;

    private readonly ILogger logger;

    public Bootstrap(
        IServer server,
        ILogger logger)
    {
        this.server = server;
        this.logger = logger;
    }

    public async Task RunAsync(CancellationToken token)
    {
        this.logger.Info("Application Starting...");

        await this.server.StartAsync(token);
    }
}

这是服务器:

public abstract class BaseServer : IServer
{
    private readonly IPAddress ipAddress;

    private readonly int port;

    private readonly ILogger logger;

    protected BaseServer(
        string ipAddress, 
        int port,
        ILogger logger)
    {
        this.ipAddress = IPAddress.Parse(ipAddress);
        this.port = port;
        this.logger = logger;
    }

    public async Task StartAsync(CancellationToken token)
    {
        this.logger.Debug("[{0}] Listening for connections using: {1}:{2}", this.GetType().Name, this.ipAddress.ToString(), this.port);
        var tcpListener = new TcpListener(this.ipAddress, this.port);

        tcpListener.Start();

        while (!token.IsCancellationRequested)
        {
            await this.ServerProcessorAsync(tcpListener, token);
        }

        tcpListener.Stop();
        Console.WriteLine("Stopped Listener");
    }

    public abstract Task ServerProcessorAsync(TcpListener listener, CancellationToken token);
}

这是服务器处理器:

public class Server : BaseServer
{
    private readonly ILogger logger;

    public Server(
        IAppConfiguration configuration,
        ILogger logger) 
        : base(configuration.IpAddress, configuration.Port, logger)
    {
        this.logger = logger;
    }

    public override async Task ServerProcessorAsync(TcpListener listener, CancellationToken token)
    {
        this.logger.Debug("[{0}] Waiting for connection...", this.GetType().Name);
        var client = await listener.AcceptTcpClientAsync();

        this.logger.Debug("[{0}] Spawning Thread for Connection...", this.GetType().Name);
        Parallel.Invoke(new ParallelOptions
            {
                CancellationToken = token,
                MaxDegreeOfParallelism = 10000,
                TaskScheduler = TaskScheduler.Current
            }, () => this.ListenToClient(client));
    }

    private void ListenToClient(TcpClient client)
    {
        var threadName = Thread.CurrentThread.Name;

        var bytes = new byte[2048];

        var stream = client.GetStream();

        int i;
        while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
        {
            var timeString = DateTime.Now.ToLongTimeString();
            var currentRes = Encoding.UTF8.GetString(bytes);
            var received = $"Recieved [{threadName}] [{timeString}]:  {currentRes}";

            this.logger.Info(received);
            var responseData = Encoding.UTF8.GetBytes(received);
            stream.Write(responseData, 0, responseData.Length);

        }

        client.Close();
    }
}

按下ctrl+c时,这会正确关闭应用吗?

有没有办法调试它,或者知道资源已经正确发布。

我认为while (!token.IsCancellationRequested)会在ctrl+c时中断。我还假设,如果Parallel.Invoke创建了任何线程,则会在调用Cancel时将其处理掉。

如果是:

Console.CancelKeyPress += (sender, eventArgs) =>
{
     TokenSource.CancelAfter(500);
};

不等待事情被清除,有没有比超时更好的方法来确保一切都被正确清理?

1 个答案:

答案 0 :(得分:1)

首先,在订阅Console.CancelKeyPress事件之前等待RunAsync ,所以当它太晚时你会订阅它。

其次,取消令牌无论如何都不会在你的情况下工作。这一行:

var client = await listener.AcceptTcpClientAsync();

在新客户端连接之前会阻止,并且因为AcceptTcpClientAsync

},所以CancellationToken不会需要在整个程序中使用CancellationTokens 。你应该做的是停止你的听众而不是取消。这将在上面的行上抛出异常,您应该捕获并优雅地结束任务。

如果您真的想继续使用CancellationToken,即使此处并不真正需要,也请考虑采用此方法使其与AcceptTcpClientAsynchttps://stackoverflow.com/a/14524565/5311735一起使用。如果您使用CancellationToken取消问题中未显示的许多不同操作,这也可能是个好主意。