2016-05-01 18 views
0

這裏的切入點:是CancellationTokenSource關停正確

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

*當ctrl + c被按下時,這會正確關閉app嗎?*您是否通過輸入Crtl + C來測試? – CodeNotFound

+0

'ctrl + c'會關閉應用程序,無論它是否正確完成都是問題。 –

回答

1

首先,您在訂閱Console.CancelKeyPress事件之前在RunAsync 之前等待,之後訂閱它,以便在它太晚時訂閱它。

其次,取消令牌無論如何不會在你的情況下。這條線:

var client = await listener.AcceptTcpClientAsync(); 

將阻塞,直到新的客戶端連接,因爲AcceptTcpClientAsync具有過載,它接受CancellationToken - 在整個節目的CancellationTokens使用變得沒有必要。你應該做的是停止你的聽衆而不是取消。這會在上面的行中拋出異常,你應該抓住並正常結束任務。

如果您確實想繼續使用CancellationToken,即使此處並不真正需要,請考慮使用此方法使其與AcceptTcpClientAsynchttps://stackoverflow.com/a/14524565/5311735配合使用。如果您使用CancellationToken來取消您的問題中未顯示的許多不同操作,這也可能是個好主意。

+0

啊,當然是!那麼我會研究一下。 –

+0

請注意,您還需要等待您的任務在CancelKeyPress事件處理程序中完成,否則在清理任何內容之前,您的進程只會停下來。 – Evk

+0

等等:您正在將_one_動作傳遞給Parallel.Invoke,這意味着Parallel.Invoke根本沒有任何用處。不知道你甚至都意味着什麼。 – Evk