以下是切入点:
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);
};
不等待事情被清除,有没有比超时更好的方法来确保一切都被正确清理?
答案 0 :(得分:1)
首先,在订阅Console.CancelKeyPress事件之前等待RunAsync ,所以当它太晚时你会订阅它。
其次,取消令牌无论如何都不会在你的情况下工作。这一行:
var client = await listener.AcceptTcpClientAsync();
在新客户端连接之前会阻止,并且因为AcceptTcpClientAsync
CancellationToken
不会需要在整个程序中使用CancellationTokens
。你应该做的是停止你的听众而不是取消。这将在上面的行上抛出异常,您应该捕获并优雅地结束任务。
如果您真的想继续使用CancellationToken,即使此处并不真正需要,也请考虑采用此方法使其与AcceptTcpClientAsync
:https://stackoverflow.com/a/14524565/5311735一起使用。如果您使用CancellationToken
取消问题中未显示的许多不同操作,这也可能是个好主意。