Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/337.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使.NET控制台应用程序保持活动状态,直到终止序列完成_C#_.net_Terminate_Windows Console_Kernel32 - Fatal编程技术网

C# 使.NET控制台应用程序保持活动状态,直到终止序列完成

C# 使.NET控制台应用程序保持活动状态,直到终止序列完成,c#,.net,terminate,windows-console,kernel32,C#,.net,Terminate,Windows Console,Kernel32,我正在开发一个数据采集应用程序,我想确保它正常退出。也就是说,它处理所有已经收集的数据,将所有(文件)缓冲区刷新到“磁盘”(持久内存),甚至可能将数据上传到云 所以,我写了(基于答案)下面的代码来捕捉每一个结束事件。(这只是一个测试代码。) 问题:如果我使用控制台右上角的X,程序会在短时间延迟后终止,即使终止序列仍在运行。(处理程序确实会被调用,它确实会开始等待线程加入,但过了一段时间它就会被终止。)如果我使用Crt+C或Ctr+Break终止,它会按预期工作;终止序列完成并退出进程 问题:如何

我正在开发一个数据采集应用程序,我想确保它正常退出。也就是说,它处理所有已经收集的数据,将所有(文件)缓冲区刷新到“磁盘”(持久内存),甚至可能将数据上传到云

所以,我写了(基于答案)下面的代码来捕捉每一个结束事件。(这只是一个测试代码。)

问题:如果我使用控制台右上角的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();
}
#区域陷阱应用程序终止
[DllImport(“内核32”)]
私有静态外部bool setConsoletrlHandler(EventHandler,bool add);
私有委托bool EventHandler(CtrlType sig);
静态EventHandler\u处理程序;
枚举CtrlType
{
CTRL\u C\u事件=0,
CTRL\u BREAK\u事件=1,
CTRL\u CLOSE\u事件=2,
CTRL\u注销\u事件=5,
CTRL\u关机\u事件=6
}
私有静态bool处理程序(CtrlType sig、列表线程、列表任务、CancellationTokenSource cancellationRequest)
{
//启动新的foregeound线程,因此进程不会在所有取消的线程结束时终止
线程关闭器=新线程(()=>terminationSequence(线程、任务、取消请求));
closer.IsBackground=false;
closer.Start();
closer.Join();//等待终止序列完成
return true;//只是为了美观;这永远不会运行(显然)
}
私有静态void terminationSequence(列出线程、列出任务、CancellationTokenSource cancellationRequest)
{
cancellationRequest.Cancel();//向所有线程和任务发送取消请求
//等待所有任务满足取消请求
foreach(任务中的任务)
{
task.Wait();
}
//等待所有踏板满足取消请求
foreach(线程中的线程)
{
thread.Join();
}
/*也许做些额外的工作*/
//模拟正在做的工作
秒表秒表=新秒表();
秒表。开始();
控制台。写入线(“旋转”);
时(秒表时间小于30秒)
{
如果(秒表运行时间.秒数%2==0)
{
Console.Clear();
WriteLine(“运行时间:{0}m{1}s”,stopwatch.appeased.Minutes,stopwatch.appeased.Seconds);
}
Thread.SpinWait(10000);
}
Environment.Exit(0);//退出进程
}
#端区
静态void Main(字符串[]参数)
{
CancellationTokenSource cancellationRequest=new CancellationTokenSource();//向所有线程和任务发送取消信号
List threads=new List();//线程列表
//指定终止处理程序
_handler+=neweventhandler((type)=>handler(type,threads,newlist(),cancellationRequest));
SetConsoleCtrlHandler(_handler,true);
//创建新线程
线程t=新线程(()=>逻辑(cancellationRequest.Token));
添加(t);
t、 Start();
}
自C#7.1以来,您可以有一个
异步任务Main()
方法。使用此方法,您可以修改处理程序以创建方法,并在
Main
方法中等待它

旁注:您应该尽可能使用任务而不是线程。任务可以更好地管理您的线程,并且可以在线程池中运行。当您创建一个新的线程实例时,它假定它将是一个长时间运行的任务,windows将以不同的方式对待它

这样,考虑在任务中,而不是一个线程中包装<代码> TealStaseSuth.<代码>方法,并使该任务<强>类的成员< /强>。现在,您不必在处理程序的上下文中等待它,而是可以在

Main
方法中等待它

在其余代码保持不变的情况下,您可以执行以下操作:

private Task _finalTask;

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
    _finalTask = Task.Run(() => terminationSequence(threads, tasks, cancellationRequest));
}

// ...

static async Task Main(string[] args)
{
    // ...

    // Wait for the termination process
    if(_finalProcess != null)
        await _finalTask
}

这应该就够了。

很抱歉,@RosdiKasim可能是重复的,但是像你这样的人把问题标记为与问题无关的问题的重复,这绝对是在破坏这个网站……也许这可以给你一些提示。看起来操作系统给你的最长时间是5秒来完成所有清理工作。@Thangadurai该特定超时由.NET运行时控制。如果他使用win32 API控制台方法,则超时时间为30秒。如果他决定上传数据,两者都不是很好。我在写一个建议,写一个Windows服务,应该让他完全控制,但是
任务
的答案要好得多。很抱歉,显然我不是唯一一个被这个问题弄糊涂的人。此外,1个标志不会关闭您的问题。也许你应该改写问题的标题。我喜欢这个答案,但我建议在边缘情况下,比如用户注销或机器关闭时,进行OP测试。我知道旧的Win32控制台API处理这些问题的方式非常不同。您应该使用
\u finalTask?.GetAwaiter().GetResult()取而代之。请参阅以了解原因。@CKII记录:我确实尝试了您的解决方案,但它不起作用。为什么会这样?(也许我错过了什么。)但是,你做的事情和我做的一样,只是方式略有不同,因此它也有同样的问题;OS/.NET框架在它完成之前就杀死了它。我看不出这有什么帮助。无论您使用任务、线程还是其他方式,操作系统都会以同样的方式终止进程。
_finalTask?.Wait();