当从其他Task引发的事件运行时,执行Task会冻结GUI,而不是从Button.Click运行



我在一个单独的项目中有一些任务要异步执行。在接下来的几个月里,一切都如预期:

  • 在开始时引发"已启动"事件,因此GUI可以启动繁忙指示器
  • 在结束时(或发生异常时)会引发"Ended"-事件因此GUI可以停止繁忙指示符并处理内容

但一个新的要求是,我必须在编程后直接阅读版本,而无需任何用户交互。

我的第一个想法是从编程的"Ended"事件调用ReadVersion。但是,执行此操作时,UI会冻结。繁忙指示器挂在"编程"状态,在读取版本之前无法移动窗口。通过按钮调用ReadVersion时。单击不会冻结GUI。

项目逻辑:

protected void ProgramImage()
{
    this.OnProgrammingStarted(new EventArgs());
    this.taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    Task.Factory.StartNew(this.ProgramImageAsync)
                .ContinueWith(
                            t =>
                            {
                                if (t.IsFaulted)
                                {
                                    this.TaskExceptionHandlerProgramming(t);
                                }
                                else
                                {
                                    this.ProgramImageAsyncDone();
                                }
                            },
                            this.taskScheduler);
}

public void ReadVersion()
{
    this.OnVersionReadingStarted(new EventArgs());
    this.taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    Task<string>.Factory.StartNew(ReadVersionAsync).ContinueWith(
        t =>
            {
                if (t.IsFaulted)
                {
                    this.TaskExceptionHandlerVersionReading(t);
                }
                else
                {
                    this.ReadVersionAsyncDone(t.Result);
                }
            },
                this.taskScheduler);
}

GUI:

private void OnProgrammingExecuted(object sender, WorkExecutedEventArgs e)
{
    if (e.Error != null)
    {
        // Logging + messagebox
        this.BiProgramming.IsBusy = false;
        this.Restart();
        return;
    }
    if (!e.Success)
    {
        // Logging + messagebox
        this.BiProgramming.IsBusy = false; 
        this.Restart();
        return;
    }
    this.BiProgramming.IsBusy = false;  // Stops busyindicator
    BL.ReadVersion();  // Window freezes and busyIndicator stays visible.
}

private void BtnReadVersionClick(object sender, RoutedEventArgs e)
{
    BL.ReadVersion(); // works as expected
}

编辑

OnProgrammingExecuted是当从ProgramImageAsyncDone内引发事件时执行的方法。

方法ProgramImage是从UI线程调用的。如果我没有错的话,这意味着TaskSheduler.FromCurrentSynchronizationContext确保ProgramImageAsyncDone(或TaskExceptionHandlerProgramming)在UI线程上运行。

事实上,它以前没有引起任何问题,加强了我的看法。

问题在于我必须添加的行:对ReadVersion的调用。添加此行之后,UI将冻结。但是从按钮调用此方法。单击不会冻结UI。

在搜索解决方案时,我遇到了Task.Run与Task.Factory.StartNew,我用Task.Run重写了我的代码,看看这是否解决了我的问题。显然是这样。

更改代码:

protected void ProgramImage()
{
    this.OnProgrammingStarted(new EventArgs());
    this.taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    Task.Run(() => this.ProgramImageAsync()).ContinueWith(
                            t =>
                            {
                                if (t.IsFaulted)
                                {
                                    this.TaskExceptionHandlerProgramming(t);
                                }
                                else
                                {
                                    this.ProgramImageAsyncDone();
                                }
                            },
                            this.taskScheduler);
}

public void ReadVersion()
{
    this.OnVersionReadingStarted(new EventArgs());
    Task.Run(() => ReadVersionAsync()).ContinueWith(
        t =>
        {
            if (t.IsFaulted)
            {
                this.TaskExceptionHandlerVersionReading(t);
            }
            else
            {
                this.ReadVersionAsyncDone(t.Result);
            }
        },
                this.taskScheduler);
}

最新更新