2017-04-25 132 views
0

我已经编写了一个WPF应用程序,它在后台执行一些处理。当应用程序关闭时,我想等到处理完成后才能停止进程。当我通过关闭表单关闭应用程序时,此代码完美运行。但是,当我离开应用程序打开结束注销我的Windows会话或重新启动机器时,应用程序似乎终止于SemaphoreSlim.Wait()。奇怪的是事件日志中没有记录错误。我尝试用try catch捕获错误,但错误仍未被捕获。如何等待一个线程完成Windows注销之前

这里是我的代码:

public partial class NotifyWindow : Window 
    { 
    StreamWriter _stream; 

    public NotifyWindow() 
    { 
     InitializeComponent(); 

     _stream = File.AppendText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log.txt")); 

     WriteLine("startup"); 
    } 

    private void WriteLine(string line) 
    { 
     var text = string.Format("{0} {1}", DateTime.Now, line); 

     lock (_stream) 
     { 
     _stream.WriteLine(text); 
     _stream.Flush(); 
     } 
    } 

    SemaphoreSlim _locker = new SemaphoreSlim(0); 

    private void CloseMe() 
    { 
     WriteLine(string.Format("CloseMe started")); 
     Thread.Sleep(1000); 

     WriteLine(string.Format("release lock")); 
     _locker.Release(); 

     WriteLine(string.Format("CloseMe finished")); 
    } 

    protected override void OnClosing(CancelEventArgs e) 
    { 
     WriteLine(string.Format("OnClosing started")); 

     base.OnClosing(e); 

     var t = new Thread(CloseMe); 
     t.Start(); 

     WriteLine("Waiting on lock."); 
     _locker.Wait(); 
     WriteLine("Waiting on lock finished."); 

     WriteLine(string.Format("OnClosing finished")); 
    } 
    } 

这导致以下日志

When application is closed by closing the window 
24-4-2017 13:57:29 startup 
24-4-2017 13:57:30 OnClosing started 
24-4-2017 13:57:30 Waiting on lock. 
24-4-2017 13:57:30 CloseMe started 
24-4-2017 13:57:31 release lock 
24-4-2017 13:57:31 CloseMe finished 
24-4-2017 13:57:31 Waiting on lock finished. 
24-4-2017 13:57:31 OnClosing finished 

When application is closed by windows logoff 
24-4-2017 13:57:43 startup 
24-4-2017 13:57:47 OnClosing started 
24-4-2017 13:57:47 Waiting on lock. 
24-4-2017 13:57:47 CloseMe started 

有没有一种方法可以让我等待代码的线程上关闭应用程序前完成?我尝试了各种(How to wait for thread to finish with .NET?)线程等待替代方案,但似乎都表现出相同的行为。

我使用.NET 4.6.2和我转载

+0

你的线程是做什么的?如果它用于将消息写入文件,则可以使用日志库或将文本附加到文件的'ActionBlock '完全替换它。当关闭应用程序或触发'SessionEnding'事件时,可以使用'ActionBlock.Complete()'方法。结果会更清晰 –

+0

我用ActionBlock重写了代码。当我们从Windows注销(关闭表单确实等待,但Windows关机不)时,问题仍然与ActionBlock.Completion.Wait()上未终止的应用程序相同。 – DesDesDes

+0

*您在'SessionEnding'上调用了'Complete()'吗?你是否删除了信号量或其他会阻止该块运行的东西? 您不需要使用'.Wait()',您可以将事件处理程序的签名更改为'async void'并写入'await block.Completion;',例如'block.Complete();等待block.Completion;'。 –

回答

1

问题是你先打电话base.close在Windows 7和Windows 10

TIA问题,

巴特弗里斯事件关闭您应该取消形式 结束当线程的任务完成了,你calll this.close但这个时候你设置的参数出口为true(定义参数)

+0

这个问题并不是由base.close引起的,因为这样在通过关闭表单关闭应用程序时它可以重现,并且仍然可以工作。当应用程序由注销关闭时,CancelEventArgs.Cancel被忽略。这是在这里记录:https://msdn.microsoft.com/en-us/library/system.windows.window.closing(v=vs.110).aspx所以,即使这是问题是不能解决的设置取消。 – DesDesDes

3

有一个Application.SessionEnding事件OC当用户通过注销或关闭操作系统来结束Windows会话时会发生。你可以尝试处理这一个。

您的应用程序无法真正控制操作系统的功能。在您的代码完成之前,用户可能仍会杀死您的应用程序,并且您对此恐怕没有太多可以做的事情。

+0

我把代码从OnClosing移动到Application.SessionEnding,但问题依然如故。如果仍然停止处理_locker.Wait(); – DesDesDes

+0

@DesDesDes信号将不会自行发出信号。 *您的代码*必须在'SessionEnding'中发信号。你不必在事件内部移动你的代码hanlder –

+0

@PanagiotisKanavos只需为自己尝试一下代码,你就会发现,Windows注销注意到邮件应用程序线程正在等待一个信号量,只是杀死了应用程序而没有写任何东西事件日志。 – DesDesDes

0

谢谢大家的反馈意见。根据您的反馈,我找到了一个解决方法,不使用任何操作系统锁定机制。这似乎工作。

下面是代码:

public partial class NotifyWindow : Window 
{ 
    StreamWriter _stream; 
    volatile int _running = 0; 

    public NotifyWindow() 
    { 
    InitializeComponent(); 

    _stream = File.AppendText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log.txt")); 

    WriteLine("startup"); 
    } 

    private void WriteLine(string line) 
    { 
    var text = string.Format("{0} {1}", DateTime.Now, line); 

    lock (_stream) 
    { 
     _stream.WriteLine(text); 
     _stream.Flush(); 
    } 
    } 

    private void CloseMe() 
    { 
    WriteLine(string.Format("CloseMe started")); 
    Thread.Sleep(1000); 

    WriteLine(string.Format("release lock")); 
    _running = 1; 

    WriteLine(string.Format("CloseMe finished")); 
    } 

    protected override void OnClosing(CancelEventArgs e) 
    { 
    WriteLine(string.Format("OnClosing started")); 

    base.OnClosing(e); 

    var t = new Thread(CloseMe); 
    t.Start(); 

    WriteLine("Waiting on lock."); 

    while(_running == 0) 
    { 
     Thread.Sleep(50); 
     WriteLine("Waiting for 50ms."); 
    } 

    WriteLine("Waiting on lock finished."); 

    WriteLine(string.Format("OnClosing finished")); 
    } 
} 

感谢大家的帮助。

相关问题