<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      WPF 警惕 StylusPlugIn 的多線程安全問題

      在 WPF 里面,即使是相同的觸摸 Id 值,都可能分別在觸摸線程或 UI 線程調入,由于多線程調度問題,可能發生觸摸事件是先 Up 后 Down 的情況

      為什么在 WPF 里面,可能在 UI 線程將觸摸消息調入 StylusPlugIn 里面?復現步驟是什么?

      本質的機制問題是 StylusPlugIn 附加到 UIElement 上,觸摸線程和 UI 線程之間無法完全實時同步狀態,如當 UI 線程執行布局等邏輯時,將會影響觸摸線程嘗試命中的結果,直接可能就是原本應該命中到給定 UIElement 上的點沒有真的命中到。為了解決此問題,在 WPF 里面添加了兜底實現邏輯,那就是在 UI 線程判斷當前消息是否已經通過 StylusPlugIn 引發了,如果未引發,則補充引發。如此就能規避丟點或不成對 Down Up 的問題

      但此兜底邏輯卻帶來了線程安全問題。這是因為 UI 線程判斷消息是否引發是通過時機來決定,如果剛好此時觸摸線程沒有被 CPU 調度,讓 UI 線程更早執行。則 UI 線程判定 StylusPlugIn 沒有引發事件,由 UI 線程執行觸發。盡管在 WPF 框架層確保相同的事件不會重復在 UI 和觸摸線程同時引發,但從原理上無法保證 Down 和 Up 的順序

      其線程安全表現就是可能在 StylusPlugIn 收到先 Move 后 Up 再 Down 的情況。其中 Move 和 Up 在一個線程引發,而 Down 在另一個線程引發

      復現步驟很簡單,只需要讓 StylusPlugIn 足夠卡頓即可,最簡復現代碼就是隨意編寫一個類型繼承 StylusPlugIn 類,在 OnStylusDown\Move\Up 方法使用 Thread.Sleep 模擬卡頓即可。我將最簡復現 Demo 代碼放在本文末尾

      再進一步,通過具體的源代碼了解此問題復現機制原理

      如下圖所示,此時觸摸消息進入 OnStylusDown 方法的是主線程

      一路追蹤堆棧,可見這是從主線程的鼠標消息進入觸發的,如下圖所示

      其調用堆棧如下

       LoqairjaniferNudalcefinay.dll!LoqairjaniferNudalcefinay.MainWindowStylusPlugIn.OnStylusUp(System.Windows.Input.StylusPlugIns.RawStylusInput rawStylusInput = {System.Windows.Input.StylusPlugIns.RawStylusInput}) 行 87  C#
        PresentationCore.dll!System.Windows.Input.StylusPlugIns.StylusPlugIn.RawStylusInput(System.Windows.Input.StylusPlugIns.RawStylusInput rawStylusInput) 行 107 C#
        PresentationCore.dll!System.Windows.Input.StylusPlugIns.StylusPlugInCollection.FireRawStylusInput.AnonymousMethod__0() 行 372  C#
        PresentationCore.dll!System.Windows.Input.StylusPlugIns.StylusPlugInCollection.ExecuteWithPotentialLock(System.Action action) 行 478 C#
        PresentationCore.dll!System.Windows.Input.StylusPlugIns.StylusPlugInCollection.FireRawStylusInput(System.Windows.Input.StylusPlugIns.RawStylusInput args = {System.Windows.Input.StylusPlugIns.RawStylusInput}) 行 370 C#
        PresentationCore.dll!System.Windows.Input.StylusWisp.WispLogic.VerifyStylusPlugInCollectionTarget(System.Windows.Input.RawStylusInputReport rawStylusInputReport = {System.Windows.Input.RawStylusInputReport}) 行 2719  C#
        PresentationCore.dll!System.Windows.Input.StylusWisp.WispLogic.PreNotifyInput(object sender, System.Windows.Input.NotifyInputEventArgs e = {System.Windows.Input.NotifyInputEventArgs}) 行 1049  C#
        PresentationCore.dll!System.Windows.Input.InputManager.ProcessStagingArea() 行 753 C#
        PresentationCore.dll!System.Windows.Input.InputManager.ProcessInput(System.Windows.Input.InputEventArgs input) 行 552  C#
        PresentationCore.dll!System.Windows.Input.StylusWisp.WispLogic.InputManagerProcessInput(object oInput) 行 377  C#
        WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)  未知
        WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source = {System.Windows.Threading.Dispatcher}, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler = null)  未知
        WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() 未知
        WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object state)  未知
        WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj) 未知
        System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) 行 137 C#
        WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext = {MS.Internal.CulturePreservingExecutionContext}, System.Threading.ContextCallback callback, object state)  未知
        WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() 未知
        WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue()  未知
        WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)  未知
        WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd = 0x04190b72, int msg, System.IntPtr wParam = 0x00000000, System.IntPtr lParam = 0x00000000, ref bool handled = false)  未知
        WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) 未知
        WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)  未知
        WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source = {System.Windows.Threading.Dispatcher}, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler = null)  未知
        WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) 未知
        WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd = 0x04190b72, int msg, System.IntPtr wParam = 0x00000000, System.IntPtr lParam = 0x00000000) 未知
        [本機到托管的轉換]  
        [托管到本機的轉換]  
        WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame = {System.Windows.Threading.DispatcherFrame})  未知
        WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) 未知
        WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() 未知
        PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) 未知
        PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window)  未知
        PresentationFramework.dll!System.Windows.Application.Run()  未知
        LoqairjaniferNudalcefinay.dll!LoqairjaniferNudalcefinay.App.Main()  未知
      

      為什么正常情況下,不會有 UI 線程調入呢?這是因為在 WispLogic.VerifyStylusPlugInCollectionTarget 里面的 sendRawStylusInput 局部變量不為 true 值。什么時候 sendRawStylusInput 會為 true 值導致 UI 線程兜底邏輯執行觸發 StylusPlugIn 的方法?如以下有刪減的代碼所示

              private void VerifyStylusPlugInCollectionTarget(RawStylusInputReport rawStylusInputReport)
              {
                  StylusPlugInCollection targetPIC = null;
                  UIElement newTarget = InputElement.GetContainingUIElement(rawStylusInputReport.StylusDevice.DirectlyOver as DependencyObject) as UIElement;
                  if (newTarget != null)
                  {
                      targetPIC = rawStylusInputReport.PenContext.Contexts.FindPlugInCollection(newTarget);
                  }
      
                     bool sendRawStylusInput = false;
                     if (targetPIC != null && rawStylusInputReport.RawStylusInput == null)
                     {
                         ... // 忽略其他代碼
      
                         sendRawStylusInput = true;
                     }
                  ... // 忽略其他代碼
              }
      

      通過以上代碼清晰可見,只有在 newTarget 能夠獲取 StylusPlugInCollection 對象,且 rawStylusInputReport.RawStylusInput 為空時才會設置 sendRawStylusInput 變量為 true 值

      如果一個 UIElement 已經附加了 StylusPlugIn 了,自然就能讓 targetPIC 變量非空。那什么時候會出現 rawStylusInputReport.RawStylusInput == null 的情況?那就是如觸摸線程因為布局時機問題,導致無法拿到 StylusPlugIn 進而導致此是空。這預計就是 WPF 兜底邏輯最核心解決的問題了。但本文提到的是另一個情況,那就是觸摸線程卡頓,導致沒有執行。沒有執行導致 rawStylusInputReport.RawStylusInput 為空。于是此時 UI 線程代為觸發

      本文代碼放在 githubgitee 上,可以使用如下命令行拉取代碼。我整個代碼倉庫比較龐大,使用以下命令行可以進行部分拉取,拉取速度比較快

      先創建一個空文件夾,接著使用命令行 cd 命令進入此空文件夾,在命令行里面輸入以下代碼,即可獲取到本文的代碼

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

      以上使用的是國內的 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令行繼續輸入以下代碼,將 gitee 源換成 github 源進行拉取代碼。如果依然拉取不到代碼,可以發郵件向我要代碼

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

      獲取代碼之后,進入 WPFDemo/LoqairjaniferNudalcefinay 文件夾,即可獲取到源代碼

      更多技術博客,請參閱 博客導航

      posted @ 2025-09-11 08:49  lindexi  閱讀(22)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产成人8x视频一区二区| 欧美视频网站www色| 国产成人精品2021欧美日韩| 日日猛噜噜狠狠扒开双腿小说| 日本无遮挡真人祼交视频| 97精品久久久大香线焦| 欧美XXXX黑人又粗又长| 亚洲精品综合第一国产综合| 国产福利在线观看免费第一福利| 成人免费A级毛片无码网站入口| 亚洲乱码国产乱码精品精| 在线观看免费人成视频色9| 67194熟妇在线观看线路| 伊人久久大香线蕉综合网| 久久亚洲精品情侣| 国产AV无码专区亚洲AWWW| 久久精品国产亚洲欧美| 中文字幕日韩有码国产| 色综合久久一区二区三区| 亚洲高清国产拍精品熟女| 婷婷99视频精品全部在线观看| 国产av亚洲一区二区| 精品久久久久久无码人妻蜜桃| 国产精品午夜福利小视频| 好爽毛片一区二区三区四| 人妻熟女一二三区夜夜爱| 久久精品国产一区二区三区| 奇米777四色成人影视| 97久久综合亚洲色hezyo| 久久久亚洲欧洲日产国码αv| 精品无人乱码一区二区三区| 国产激情艳情在线看视频| 丰满人妻熟妇乱又仑精品| 国产免费视频一区二区| 黄色大全免费看国产精品| 中文字幕国产精品资源| 野花社区www高清视频| 亚洲精品日本一区二区| 欧美乱码精品一区二区三区| 巴塘县| 免费人成在线观看网站|