UI跨线程操作异常后Task.ConfigureAwait行为 [英] Task.ConfigureAwait behavior after UI cross-thread operation exception

查看:157
本文介绍了UI跨线程操作异常后Task.ConfigureAwait行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在和Task.ConfigureAwait玩耍,以便更好地了解引擎盖之外的内容.因此,在将某些UI访问内容与ConfigureAwait结合使用时,我得到了这种奇怪的行为.

I was playing with Task.ConfigureAwait in order to better understand what is going beyond the hood. So i got this strange behavior while combining some UI access stuff with the ConfigureAwait.

下面是使用简单Windows窗体的示例应用程序,其中包含1个Button,其后是测试结果:

Below is the sample app using a simple windows form, with 1 Button followed by the test results:

private async void btnDoWork_Click(object sender, EventArgs e)
{
    List<int> Results = await SomeLongRunningMethodAsync().ConfigureAwait(false);

    int retry = 0;
    while(retry < RETRY_COUNT)
    {
        try
        {
            // commented on test #1 & #3 and not in test #2
            //if(retry == 0)
                //throw new InvalidOperationException("Manually thrown Exception");

            btnDoWork.Text = "Async Work Done";
            Logger.Log("Control Text Changed", logDestination);
            return;
        }
        catch(InvalidOperationException ex)
        {
            Logger.Log(ex.Message, logDestination);
        }

        retry++;
    }
}

按钮后点击:

测试1日志结果:(与上面的代码完全相同)

Test 1 Log results : (Exactly as the above code)

1. Cross-thread operation not valid: Control 'btnDoWork' accessed from a thread other than the thread it was created on.
2. Control Text Changed

测试2日志结果:(手动注释未注释)

1.  Manually thrown Exception
2.  Cross-thread operation not valid: Control 'btnDoWork' accessed from a thread other than the thread it was created on.
3.  Control Text Changed

测试3个日志结果:(与1相同,但没有调试器)

Test 3 Log results : (Same as 1 but without a debugger)

1. Control Text Changed

所以问题是:

  1. 为什么第一次进行UI访问(跨线程 操作),使循环的下一次迭代在Main上执行 线 ?

  1. Why does the first UI Access (Cross-Thread Operation) have the next iteration of the loop execute on the Main Thread ?

为什么手动异常不会导致相同的行为?

Why doesn't the manual exception lead to the same behavior ?

为什么在没有附加调试器的情况下直接执行上述示例(直接来自 exe ) 没有表现出相同的行为?

Why does executing the above sample without a debugger attached (directly from exe) doesn't show the same behavior ?

推荐答案

这个让我有些挠头,但终于找到了窍门.

This one got me scratching my head a bit, but finally found the trick.

Button.Text属性的设置器的代码为:

The code of the setter of the Button.Text property is:

  set
  {
    if (value == null)
      value = "";
    if (value == this.Text)
      return;
    if (this.CacheTextInternal)
      this.text = value;
    this.WindowText = value;
    this.OnTextChanged(EventArgs.Empty);
    if (!this.IsMnemonicsListenerAxSourced)
      return;
    for (Control control = this; control != null; control = control.ParentInternal)
    {
      Control.ActiveXImpl activeXimpl = (Control.ActiveXImpl) control.Properties.GetObject(Control.PropActiveXImpl);
      if (activeXimpl != null)
      {
        activeXimpl.UpdateAccelTable();
        break;
      }
    }
  }

引发异常的行是this.WindowText = value;(因为它在内部尝试访问按钮的Handle属性).诀窍在于,就在之前,它在某种缓存中设置了text属性:

The line throwing the exception is this.WindowText = value; (because it internally tries to access the Handle property of the button). The trick is that, right before, it sets the text property in some kind of cache:

if (this.CacheTextInternal)
   this.text = value;

说实话,我不知道此缓存的工作方式以及何时激活(或不激活)(事实证明,在这种情况下似乎已激活).但是正因为如此,即使引发了异常,文本也会被设置.

I'll be honest, I have no clue how this cache works, or when it is activated or not (turns out, it seems to be activated in this precise case). But because of this, the text is set even though the exception was thrown.

在循环的进一步迭代中,什么都没有发生,因为该属性经过特殊检查以确保您不会两次设置相同的文本:

On further iterations of the loop, nothing happens because the property has a special check to make sure you don't set the same text twice:

if (value == this.Text)
  return;

如果您将循环更改为每次设置不同的文本,那么您将看到在每次迭代中一致地引发异常.

If you change your loop to set a different text every time, then you'll see that the exception is thrown consistently at each iteration.

这篇关于UI跨线程操作异常后Task.ConfigureAwait行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆