【发布时间】:2018-08-19 14:28:42
【问题描述】:
我正在开发一个数据采集应用程序,我想确保它正常退出。也就是说,它处理所有已经收集的数据,将所有(文件)缓冲区刷新到“磁盘”(持久内存),甚至可能将数据上传到云端。
所以,我编写了(基于this 答案)下面的代码来捕获每个关闭事件。 (这只是一个测试代码。)
问题:如果我使用控制台右上角的 X,程序会在短暂延迟后终止,即使终止序列仍在运行。 (处理程序确实被调用了,它确实开始等待线程加入,但过了一会儿它就被杀死了。)如果我用 Crt+C 或 Ctr+Break 终止,它会按预期工作;终止序列完成并退出进程。
问题:如何让操作系统等待我的应用程序终止,而不是在短暂的宽限期后将其终止?
#region Trap application termination
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler _handler;
enum CtrlType
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
private static bool Handler(CtrlType sig, List<Thread> threads, List<Task> tasks, CancellationTokenSource cancellationRequest)
{
//starts new foregeound thread, so the process doesn't terminate when all the cancelled threads end
Thread closer = new Thread(() => terminationSequence(threads, tasks, cancellationRequest));
closer.IsBackground = false;
closer.Start();
closer.Join(); //wait for the termination sequence to finish
return true; //just to be pretty; this never runs (obviously)
}
private static void terminationSequence(List<Thread> threads, List<Task> tasks, CancellationTokenSource cancellationRequest)
{
cancellationRequest.Cancel(); //sends cancellation requests to all threads and tasks
//wait for all the tasks to meet the cancellation request
foreach (Task task in tasks)
{
task.Wait();
}
//wait for all the treads to meet the cancellation request
foreach (Thread thread in threads)
{
thread.Join();
}
/*maybe do some additional work*/
//simulate work being done
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine("Spinning");
while (stopwatch.Elapsed.Seconds < 30)
{
if (stopwatch.Elapsed.Seconds % 2 == 0)
{
Console.Clear();
Console.WriteLine("Elapsed Time: {0}m {1}s", stopwatch.Elapsed.Minutes, stopwatch.Elapsed.Seconds);
}
Thread.SpinWait(10000);
}
Environment.Exit(0); //exit the process
}
#endregion
static void Main(string[] args)
{
CancellationTokenSource cancellationRequest = new CancellationTokenSource(); //cancellation signal to all threads and tasks
List<Thread> threads = new List<Thread>(); //list of threads
//specifys termination handler
_handler += new EventHandler((type) => Handler(type, threads, new List<Task>(), cancellationRequest));
SetConsoleCtrlHandler(_handler, true);
//creating a new thread
Thread t = new Thread(() => logic(cancellationRequest.Token));
threads.Add(t);
t.Start();
}
【问题讨论】:
-
@RosdiKasim 很抱歉,但像您这样将问题标记为与所讨论的问题无关的问题重复的人绝对会毁了这个网站......
-
可能这个SO post能给你一些提示。看起来操作系统给你的最长时间是 5 秒来完成所有的清理工作。
-
@Thangadurai 特定超时由 .NET 运行时控制。如果他使用 win32 API 控制台方法,超时为 30 秒。如果他决定上传数据,两者都不是很好。我正在写一个建议来编写一个应该让他完全控制的 Windows 服务,但是
Task的答案要好得多。 -
对此感到抱歉,但显然我不是唯一一个对这个问题感到困惑的人。此外,1 个标志不会让您的问题结束。也许你应该改写问题标题。
标签: c# .net terminate windows-console kernel32