前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WPF程序在shutdown期间引发的TaskCanceledException

WPF程序在shutdown期间引发的TaskCanceledException

作者头像
黄腾霄
发布2020-06-10 10:54:25
8350
发布2020-06-10 10:54:25
举报
文章被收录于专栏:黄腾霄的博客

先上堆栈

代码语言:javascript
复制
TaskCanceledException at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at 
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at 
System.Windows.Threading.DispatcherOperation.Wait(TimeSpan timeout) at
System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherOperation operation, CancellationToken cancellationToken, TimeSpan timeout) at 
System.Windows.Threading.Dispatcher.Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout) at
MS.Internal.WeakEventTable.OnShutDown() at 
MS.Internal.ShutDownListener.HandleShutDown(Object sender, EventArgs e) 

##背景

问题是这样的,从5月份开始,陆续有公司发现自己的WPF软件收到大量用户报告TaskCanceledException 异常,

就是上面堆栈中的信息,有的公司一个月内达到了惊人的150k的异常数据。我们的软件也在当月报了15k的异常。

诱因

原因来自于微软的18年6月预览版质量汇总补丁(KB 4229726),所以就是微软更新更炸了。

官方描述如下:

代码语言:javascript
复制
In certain .NET applications, timing issues in the finalizer thread could potentially cause exceptions during AppDomain or process shutdown.  This is generally seen in applications that do not correctly shut down Dispatchers running on worker threads prior to process or AppDomain shutdown.  Such applications should take care to properly manage the lifetime of Dispatchers. 

翻译过来就是

对于某些特定的.NET应用程序(注:目前仅影响WPF),在AppDomain或者进程关闭时,Finalizer线程的计时问题可能会引发异常。这个问题通常出现在关闭期间,这些应用程序未能够正常关闭工作线程的Dispatcher。因此这些应用需要合理的管理Dispatcher的生命周期。

根因

时机问题来源:WeakEventTableOnShutDown()方法中,指定了300ms的超时(注:部分开发评论这个时间是arbitrary 武断的)

代码如下:

代码语言:javascript
复制
private void OnShutDown()
{
    if (CheckAccess())
    {
        Purge(true);
 
        // remove the table from thread storage
        _currentTable = null;
    }
    else
    {
        // if we're on the wrong thread, try asking the right thread
        // to do the job.  (DomainUnload arrives on finalizer thread - DDVSO 543980)
        bool succeeded = false;
        try
        {
            Dispatcher.Invoke((Action)OnShutDown, DispatcherPriority.Send, CancellationToken.None, TimeSpan.FromMilliseconds());
            succeeded = true;
        }
        catch (TimeoutException)
        {
        }
 
        // if that didn't work (because Dispatcher was busy or not pumping),
        // do the work on the wrong thread, but don't touch thread-statics.
        // This won't do everything, but it will do enough to support
        // some useful scenarios (such as DevDiv Bugs 121070).
        if (!succeeded)
        {
            Purge(true);
        }
    }
}

可以看到,在错误线程调用该方法时,进入else,然后触发超时。源代码中针对TimeoutException进行了catch,但是没有处理TaskCanceledException 。而从堆栈信息上看,很可能这次更新将内部实现改为了异步任务。

影响范围

按官方文档解释,目前仅影响4.7.2上运行的部分WPF程序

解决方案

直接方案

这个补丁上线时,提供了一个开关。只要在app.config里面添加

代码语言:javascript
复制
<configuration>
    <runtime>
        <AppContextSwitchOverrides value="Switch.MS.Internal.DoNotInvokeInWeakEventTableShutdownListener=true"/>
    </runtime>
</configuration>

该方案能够有助于缓解(alleviate)该问题, 而并不能消除(eliminate)

根本方案

1、清理代码中跨线程调用OnShutDown()方法

2、减少关闭期间Dispatcher的调用

参考链接:


本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/WPF%E7%A8%8B%E5%BA%8F%E5%9C%A8shutdown%E6%9C%9F%E9%97%B4%E5%BC%95%E5%8F%91%E7%9A%84TaskCanceledException.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接: https://xinyuehtx.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-08-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 诱因
  • 根因
  • 影响范围
  • 解决方案
    • 直接方案
      • 根本方案
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档