窗口关闭事件中的Threading.task和Dispatcher.Invoke方法

3
我希望在关闭基于MVVM的WPF应用程序时同步执行一系列操作。
目前,我正在使用任务和在任务中使用Dispatcher.Invoke来向用户显示消息。
问题是当我在myfunction函数中使用Dispatcher.Invoke方法时,应用程序会卡在那里。我知道这个函数在使用除关闭事件以外的其他函数时正常工作。
那么,在关闭应用程序的事件中使用Dispatcher.Invoke方法是否有问题?我该如何解决这个问题?
/// <summary>
/// Main window closing
/// </summary>
private void MainWindowClosing(object args)
{
     var task1 = System.Threading.Tasks.Task.Factory.StartNew(() =>
     {
         myfunction();
     }).ContinueWith((cc) => {  });
     task1.Wait();
}

private void myfunction()
{
    //my serries of operation will come here.
    System.Windows.Application.Current.Dispatcher.Invoke(new Action(() =>
    {
        MessageBox.Show("test");
    }));   
}

可能是Blocking UI Thread in MVVM的重复问题。 - Ilya Tereschuk
3个回答

3

您在这里创建了一个死锁。即使将代码放在按钮点击处理程序中也无法工作。

原因

您正在创建一个任务并使用task1.Wait()等待任务完成,因此UI调度程序正在等待任务完成后才能处理任何进一步的消息。

同时,在任务中还同步地调用了UI调度程序上的某个操作。

        App.Current.Dispatcher.Invoke(new Action(() =>
            {
                MessageBox.Show("test");
            })); 

但是由于UI调度程序仍在等待任务完成,因此您无法在其上同步调用任何方法。因此会发生死锁(互相等待)。

可能的解决方案

如果您想要同步调用任务,而且还不想关闭窗口,可以按照以下步骤实现:

  1. 首先删除 task1.Wait()。(不要阻塞UI调度程序)
  2. 其次,维护一个bool变量来记录关闭事件是否已启动。
  3. 最后,在任务本身中设置 e.Cancel = true 并手动引发关闭事件,以取消关闭事件。

相关代码:

    bool closeInitiated = false;
    private void poc_Closing(object sender, CancelEventArgs e)
    {
        if (!closeInitiated)
        {
            var task1 = System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                myfunction();
            }).ContinueWith((cc) => { });
            closeInitiated = true;
            e.Cancel = true;
        }
    }

    private void myfunction()
    {
        App.Current.Dispatcher.Invoke(new Action(() =>
            {
                MessageBox.Show("test");
                Close();
            }));
    } 

0

我遇到了同样的问题,我发现问题在于我调用的任务在点击关闭按钮后一秒钟就已经关闭了,所以当调用函数需要更长时间时,它可能会崩溃。

如果你仍然想执行这个函数,我使用了BeginInvoke而不是Invoke,它可以工作。

这是我的代码:

if (app.Current.Dispatcher.CheckAccess()) //doesn't need to be invoked
    myFunction();
    else //coming from another thread need invoker
    {
        //at exiting program this task might already be canceled, so make sure it's not
        if (!app.Current.Dispatcher.HasShutdownFinished) 
            app.Current.Dispatcher.Invoke(myFunction());
        else   
            app.Current.Dispatcher.BeginInvoke(myFunction());

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接