Winform同步调用异步函数死锁原因分析、为什么要用异步 (2)

BeginInvoke源码:

//源码地址: //https://github.com/dotnet/winforms/blob/release/5.0/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs#L4678 private object MarshaledInvoke(Control caller, Delegate method, object[] args, bool synchronous) { if (!IsHandleCreated) { throw new InvalidOperationException(SR.ErrorNoMarshalingThread); } ActiveXImpl activeXImpl = (ActiveXImpl)Properties.GetObject(s_activeXImplProperty); // We don't want to wait if we're on the same thread, or else we'll deadlock. // It is important that syncSameThread always be false for asynchronous calls. bool syncSameThread = false; if (User32.GetWindowThreadProcessId(this, out _) == Kernel32.GetCurrentThreadId()) { if (synchronous) { syncSameThread = true; } } ExecutionContext executionContext = null; if (!syncSameThread) { executionContext = ExecutionContext.Capture(); } ThreadMethodEntry tme = new ThreadMethodEntry(caller, this, method, args, synchronous, executionContext); lock (this) { if (_threadCallbackList is null) { _threadCallbackList = new Queue(); } } lock (_threadCallbackList) { if (s_threadCallbackMessage == User32.WM.NULL) { // 注册消息返回消息标识(int) s_threadCallbackMessage = User32.RegisterWindowMessageW(Application.WindowMessagesVersion + "_ThreadCallbackMessage"); } // 将回调函数执行信息添加到回调函数队列 _threadCallbackList.Enqueue(tme); } // 同一个线程则直接执行 if (syncSameThread) { InvokeMarshaledCallbacks(); } else { // 将一个消息放入(寄送)到与指定窗口创建的线程相联系消息队列里 User32.PostMessageW(this, s_threadCallbackMessage); } if (synchronous) { if (!tme.IsCompleted) { WaitForWaitHandle(tme.AsyncWaitHandle); } if (tme._exception != null) { throw tme._exception; } return tme._retVal; } else { return tme; } } WndProc

应用程序中定义的回调函数,用于处理发送到窗口的消息。

示例中的代码:

/// <summary> /// 重写接收窗口的消息的回调函数 /// </summary> /// <param></param> protected override void WndProc(ref Message m) { if (m.Msg == GetThreadCallbackMessage()) { var threadCallbackList = GetThreadCallbackList(); PrintInfo($"触发WndProc:msg={m.Msg},threadCallbackList.Count={threadCallbackList.Count}"); base.WndProc(ref m); } else { base.WndProc(ref m); } } /// <summary> /// 获取需要在Ui线程执行的回调委托队列 /// </summary> /// <returns></returns> private System.Collections.Queue GetThreadCallbackList() { var threadCallbackListFiled = typeof(Control).GetField("_threadCallbackList", BindingFlags.NonPublic | BindingFlags.Instance); return (System.Collections.Queue)threadCallbackListFiled.GetValue(this); } private static int _threadCallbackMessage = 0; /// <summary> /// 获取触发回调委托的窗口消息标识 /// </summary> /// <returns></returns> private int GetThreadCallbackMessage() { if (_threadCallbackMessage == 0) { var threadCallbackMessageFiled = typeof(Control).GetField("s_threadCallbackMessage", BindingFlags.NonPublic | BindingFlags.Static); _threadCallbackMessage = Convert.ToInt32(threadCallbackMessageFiled.GetValue(null)); } return _threadCallbackMessage; }

WndProc源码:

WndProc接收到s_threadCallbackMessage消息触发执行队列_threadCallbackList的消息。

//源码地址: //https://github.com/dotnet/winforms/blob/release/5.0/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs#L12681 /// <summary> /// Base wndProc. All messages are sent to wndProc after getting filtered /// through the preProcessMessage function. Inheriting controls should /// call base.wndProc for any messages that they don't handle. /// </summary> protected virtual void WndProc(ref Message m) { // 此处省略代码未知行 // If you add any new messages below (or change the message handling code for any messages) // please make sure that you also modify AxHost.WndProc to do the right thing and intercept // messages which the Ocx would own before passing them onto Control.WndProc. switch ((User32.WM)m.Msg) { // 此处省略代码未知行 default: // If we received a thread execute message, then execute it. if (m.Msg == (int)s_threadCallbackMessage && m.Msg != 0) { InvokeMarshaledCallbacks(); return; } break; // 此处省略代码未知行 } // 此处省略代码未知行 } /// <summary> /// Called on the control's owning thread to perform the actual callback. /// This empties this control's callback queue, propagating any exceptions /// back as needed. /// </summary> private void InvokeMarshaledCallbacks() { ThreadMethodEntry current = null; lock (_threadCallbackList) { if (_threadCallbackList.Count > 0) { current = (ThreadMethodEntry)_threadCallbackList.Dequeue(); } } // Now invoke on all the queued items. while (current != null) { if (current._method != null) { try { // If we are running under the debugger, don't wrap asynchronous // calls in a try catch. It is much better to throw here than pop up // a thread exception dialog below. if (NativeWindow.WndProcShouldBeDebuggable && !current._synchronous) { InvokeMarshaledCallback(current); } else { try { InvokeMarshaledCallback(current); } catch (Exception t) { current._exception = t.GetBaseException(); } } } finally { current.Complete(); if (!NativeWindow.WndProcShouldBeDebuggable && current._exception != null && !current._synchronous) { Application.OnThreadException(current._exception); } } } lock (_threadCallbackList) { if (_threadCallbackList.Count > 0) { current = (ThreadMethodEntry)_threadCallbackList.Dequeue(); } else { current = null; } } } } 3、async deadlock代码解析 死锁代码示例、反编译代码查看

打开链接查看反编译代码:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzsggs.html