UNO 已知問題 在後臺執行緒觸發 SKXamlCanvas 的 Invalidate 且在 PaintSurface 事件丟擲異常將炸掉應用

lindexi發表於2024-09-12

本文記錄一個 UNO 已知問題,在 UNO 裡面可以利用 SKXamlCanvas 對接 Skia 繪製到應用裡面。如果此時在後臺執行緒裡面呼叫 SKXamlCanvas 的 Invalidate 觸發介面的重新重新整理,但在具體的執行繪製 PaintSurface 事件裡面對外丟擲異常,將會導致應用炸掉

背景: 我準備在 UNO 裡面將 Microsoft.Maui.Graphics 對接進入,於是就用到了 SKXamlCanvas 控制元件。詳細請看 https://github.com/unoplatform/uno/discussions/15097

當前行為: 當我使用 SKXamlCanvas 時,如果我在 PaintSurface 事件裡面丟擲任何異常,且當前的 PaintSurface 事件是由後臺執行緒觸發的,那將導致我的程序崩潰

預期行為:即使在 PaintSurface 事件裡面丟擲任何異常,應用程式也可以正常工作且收集到異常,比如透過 TaskScheduler.UnobservedTaskException 事件收集到異常

復現步驟:

  1. 新增 SKXamlCanvas 到 xaml 裡
  2. 訂閱 SKXamlCanvas 的 PaintSurface 事件,且在事件實現方法丟擲異常
  3. 在後臺執行緒呼叫 SKXamlCanvas 的 Invalidate 方法

核心的程式碼實現如下

在 XAML 新增 SKXamlCanvas 控制元件

  xmlns:sk="using:SkiaSharp.Views.Windows"

  <sk:SKXamlCanvas x:Name="Canvas" PaintSurface="OnPaintSurface" />

在後臺程式碼裡面使用後臺執行緒呼叫 SKXamlCanvas 的 Invalidate 方法,且在 OnPaintSurface 丟擲異常

            Task.Run(() =>
            {
                Canvas.Invalidate();
            });

        private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
        {
            throw new Exception();
        }

本文以上的復現程式碼放在githubgitee 歡迎訪問

可以透過如下方式獲取以上的復現程式碼,先建立一個空資料夾,接著使用命令列 cd 命令進入此空資料夾,在命令列裡面輸入以下程式碼,即可獲取到本文的程式碼

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin dde76effc23ebb9ee974b6ec276b242c39a50bdf

以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令列繼續輸入以下程式碼

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin dde76effc23ebb9ee974b6ec276b242c39a50bdf

獲取程式碼之後,進入 JagobawearjiNeewhiqakerki 資料夾

報告地址: https://github.com/unoplatform/uno/issues/15123

原因:

這是由於在 SkiaSharp 裡面的錯誤實現導致踩到 dotnet 的另一個已知問題導致的。在 SKXamlCanvas 的具體實現裡面,透過 async void 等待執行結果,而根據 dotnet 的已知問題可以知道,在 async void 收到任何異常都會導致程序崩潰,此行為詳細請參閱 dotnet 警惕 async void 執行緒頂層異常

解決方法:

此問題已經被我修復,詳細請看 Avoid async void in SKXamlCanvas. by lindexi · Pull Request #2720 · mono/SkiaSharp

相關文章