【问题标题】:How can I wait on a thread to finish before windows logoff如何在 Windows 注销之前等待线程完成
【发布时间】:2017-04-25 12:29:07
【问题描述】:

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

这是我的代码:

  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,我在 windows 7 和 windows 10 上重现了这个问题。

TIA,

巴特·弗里斯

【问题讨论】:

  • 你的线程是做什么的?如果它用于将消息写入文件,则可以将其完全替换为日志库或将文本附加到文件的ActionBlock<string>。当您关闭应用程序或触发SessionEnding 事件时,您可以使用ActionBlock.Complete() 方法。结果会干净很多
  • 我已经使用 ActionBlock 重写了代码。当我们从 Windows 注销时,问题仍然存在,因为应用程序不会在 ActionBlock.Completion.Wait() 上终止(关闭表单确实等待正确,但 Windows 关闭没有)。
  • 是否你打电话给Complete()SessionEnding?您是否删除了信号量或其他任何会阻止该块工作的东西?不需要使用.Wait(),可以将事件处理程序的签名改为async void并写成await block.Completion;,例如block.Complete(); await block.Completion;
  • @PanagiotisKanavos 尝试了所有方法,但仍然无法正常工作。与上面的代码中存在相同的问题。当 Windows 注销过程中邮件线程似乎被锁定时,即使等待仅 1 秒,它也会终止应用程序。

标签: c# wpf multithreading logoff


【解决方案1】:

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

您的应用程序无法真正控制操作系统的功能。用户可能仍然会在您的代码完成之前杀死您的应用程序,恐怕您对此无能为力。

【讨论】:

  • 我将代码从 OnClosing 移动到 Application.SessionEnding 但问题仍然存在。如果仍然停止处理 _locker.Wait();
  • @DesDesDes 信号量不会自行发出信号。 您的代码 必须在SessionEnding 中发出信号。您不必在事件处理程序中移动代码
  • @PanagiotisKanavos 自己尝试一下代码,您会发现,Windows 注销会以某种方式注意到邮件应用程序线程正在等待信号量,并且在没有在事件日志中写入任何内容的情况下直接终止应用程序。跨度>
  • @DesDesDes 您错过了 mm8 充分解释的要点。您不能强制操作系统以特定方式工作。您不能阻止操作系统 - SessionEnding 旨在让您的应用程序在固定时间段内执行一些快速清理。 NOT 不是为了阻止操作系统,也不会。如果清理时间过长,操作系统将关闭应用程序。如果应用程序被阻止,操作系统会通知(当然是操作系统,信号量由 it 提供)并知道您的代码将违反分配的清理时间
【解决方案2】:

问题是你在事件关闭时首先调用 base.close 你应该取消表单的关闭 线程作业完成后,您调用 this.close,但这次您将参数 exit 设置为 true(定义参数)

【讨论】:

  • 问题不是由 base.close 引起的,因为这样在通过关闭表单关闭应用程序时它可以重现并且仍然有效。当应用程序因注销而关闭时,将忽略 CancelEventArgs.Cancel。这在此处记录:msdn.microsoft.com/en-us/library/… 因此,即使这是问题,也无法通过设置取消来解决。
【解决方案3】:

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

代码如下:

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"));
  }
}

感谢大家的帮助。

【讨论】:

    猜你喜欢
    • 2010-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多