記一次 .NET 某工控自動化控制系統 卡死分析

一:背景

1. 講故事

前段時間遇到了好幾起關于窗體程序的 進程加載鎖 引發的 程序卡死線程暴漲 問題,這種 dump 分析難度較大,主要涉及到 Windows操作系統 和 C++ 的基礎知識,所以有必要簡單整理和大家分享一下,上 windbg 說話。

二:WinDbg 分析

1. 主線程此時在做什么

窗體程序的卡死,入口分析點在 主線程 上,使用 ~0s; k 命令即可。

0:000>?~0s;?k
ntdll!NtWaitForSingleObject+0x14:
00007ffc`6010e614?c3??????????????ret#?Child-SP??????????RetAddr???????????????Call?Site
00?0000008c`107fe5d8?00007ffc`5cda4313?????ntdll!NtWaitForSingleObject+0x14
01?0000008c`107fe5e0?00007ffc`257b2fe8?????KERNELBASE!WaitForSingleObjectEx+0x93
02?0000008c`107fe680?00007ffc`257b2f9e?????clr!CLREventWaitHelper2+0x3c
03?0000008c`107fe6c0?00007ffc`257b2efc?????clr!CLREventWaitHelper+0x1f
04?0000008c`107fe720?00007ffc`256beed2?????clr!CLREventBase::WaitEx+0x71
05?0000008c`107fe7b0?00007ffc`25687e44?????clr!WKS::GCHeap::WaitUntilGCComplete+0x2e
06?0000008c`107fe7e0?00007ffc`25688092?????clr!Thread::RareDisablePreemptiveGC+0x18f
07?0000008c`107fe880?00007ffc`255d44f4?????clr!JIT_RareDisableHelperWorker+0x42
08?0000008c`107fe9d0?00007ffc`22544314?????clr!JIT_RareDisableHelper+0x14
09?0000008c`107fea10?00007ffc`22525f32?????WindowsBase_ni+0x184314
0a?0000008c`107fead0?00007ffc`22520298?????WindowsBase_ni+0x165f32
0b?0000008c`107feb10?00007ffc`2251edaf?????WindowsBase_ni+0x160298
0c?0000008c`107feba0?00007ffc`202b6421?????WindowsBase_ni+0x15edaf
...

從卦象中的 WaitUntilGCComplete 函數看,此時的主線程正在等待 GC完成,那到底誰觸發了 GC 呢? 接下來用 !t 命令查看下 GC 標記。

0:000>?!t
ThreadCount:??????58
UnstartedThread:??9
BackgroundThread:?39
PendingThread:????9
DeadThread:???????5
Hosted?Runtime:???no42???41??cd8?000001ec5f7f7c90??202b220?Preemptive??0000000000000000:0000000000000000?000001ec3353c710?0?????MTA?43???34?1160?000001ec5f7f4db0????21220?Preemptive??0000000000000000:0000000000000000?000001ec3353c710?0?????Ukn?44???33?218c?000001ec5f7f5580????2b220?Cooperative?0000000000000000:0000000000000000?000001ec3353c710?1?????MTA?(GC)?45???36?1110?000001ec5f7f8460??202b220?Preemptive??0000000000000000:0000000000000000?000001ec3353c710?0?????MTA?48???32?26a8?000001ec545813e0????2b220?Preemptive??0000000000000000:0000000000000000?000001ec3353c710?0?????MTA?49???31??4b4?000001ec54581bb0????2b220?Preemptive??0000000000000000:0000000000000000?000001ec3353c710?0?????MTA

從卦中看,當前的 44 號線程觸發了 GC,接下來看下它的線程棧情況。

0:000>?~~[218c]s
ntdll!NtWaitForSingleObject+0x14:
00007ffc`6010e614?c3??????????????ret
0:044>?k#?Child-SP??????????RetAddr???????????????Call?Site
00?0000008c`0a0bd9b8?00007ffc`5cda4313?????ntdll!NtWaitForSingleObject+0x14
01?0000008c`0a0bd9c0?00007ffc`257b2fe8?????KERNELBASE!WaitForSingleObjectEx+0x93
02?0000008c`0a0bda60?00007ffc`257b2f9e?????clr!CLREventWaitHelper2+0x3c
03?0000008c`0a0bdaa0?00007ffc`257b2efc?????clr!CLREventWaitHelper+0x1f
04?0000008c`0a0bdb00?00007ffc`256c821d?????clr!CLREventBase::WaitEx+0x71
05?0000008c`0a0bdb90?00007ffc`256c8120?????clr!standalone::`anonymous?namespace'::CreateSuspendableThread+0x10c
06?0000008c`0a0bdc50?00007ffc`257b9e4c?????clr!GCToEEInterface::CreateThread+0x170
07?0000008c`0a0bde40?00007ffc`257b8543?????clr!WKS::gc_heap::prepare_bgc_thread+0x4c
08?0000008c`0a0bde70?00007ffc`256be9f7?????clr!WKS::gc_heap::garbage_collect+0xfbb37
09?0000008c`0a0bdeb0?00007ffc`256c0c47?????clr!WKS::GCHeap::GarbageCollectGeneration+0xef
0a?0000008c`0a0bdf00?00007ffc`255dc7b3?????clr!WKS::GCHeap::Alloc+0x29c
0b?0000008c`0a0bdf50?00007ffb`c631853d?????clr!JIT_New+0x339

從線程棧看,GC 在觸發的過程中準備使用 CreateThread 函數創建線程,可能有些朋友不太理解,GC觸發還有創建線程的操作???哈哈,這就涉及到一點 CLR 的基礎知識,workstation 的 bgc 模式會有一個專門的 后臺線程, 而這個后臺線程是在運行時的某個時刻創建和銷毀的,所以從線程棧看,GC 正在等待 bgc 線程初始化完畢。

很奇怪的是,我從多個卡死狀態下的 dump 看,發現 GC 都卡在這個 CreateThread 函數上,言外之意線程在這里無限期等待了。

2. CreateThread 為什么不能初始化完成?

如果大家玩過 C++ 的話,應該知道 C++ 的 dll 會有一個 DllMain 方法,它的意義和 Main 方法一致,代碼骨架圖如下:

//?dllmain.cpp?:?Defines?the?entry?point?for?the?DLL?application.
#include?"pch.h"BOOL?APIENTRY?DllMain(?HMODULE?hModule,DWORD??ul_reason_for_call,LPVOID?lpReserved)
{switch?(ul_reason_for_call){case?DLL_PROCESS_ATTACH:case?DLL_THREAD_ATTACH:case?DLL_THREAD_DETACH:case?DLL_PROCESS_DETACH:break;}return?TRUE;
}

從 switch 中的枚舉參數來看,就是 dll 加載和卸載,線程創建和銷毀,有此 DllMain 方法的 dll 都會收到通知,在進入到這個 DllMain 之前會首先獲取到一個全局的 進程加載鎖(LdrpLoaderLock)

既然 GC 過程中不能創建 CreateThread,那必然有人在持有這個 LdrpLoaderLock 鎖,接下來的問題就是如何找到 哪個線程正在持有 LdrpLoaderLock ?這就涉及到 windows 操作系統的 基礎知識了。

3. 誰在持有 LdrpLoaderLock 鎖?

LdrpLoaderLock 變量是在 ntdll.dll 用戶態網關dll中,可以用 x ntdll!LdrpLoaderLock 命令檢索,然后看下是作為哪個臨界區持有的。

0:044>??x?ntdll!LdrpLoaderLock
00007ffc`601cf4f8?ntdll!LdrpLoaderLock?=?<no?type?information>0:044>?dt?_RTL_CRITICAL_SECTION??00007ffc`601cf4f8
atl100!_RTL_CRITICAL_SECTION+0x000?DebugInfo????????:?0x00007ffc`601cf978?_RTL_CRITICAL_SECTION_DEBUG+0x008?LockCount????????:?0n-2+0x00c?RecursionCount???:?0n1+0x010?OwningThread?????:?0x00000000`0000138c?Void+0x018?LockSemaphore????:?(null)?+0x020?SpinCount????????:?0x4000000

從卦中看,當前 138c 號線程持有了這個臨界區,接下來切到這個線程看下它的線程棧即可。

0:044>?~~[138c]s
win32u!NtUserMessageCall+0x14:
00007ffc`5c891184?c3??????????????ret
0:061>?k#?Child-SP??????????RetAddr???????????????Call?Site
00?0000008c`00ffec68?00007ffc`5f21bfbe?????win32u!NtUserMessageCall+0x14
01?0000008c`00ffec70?00007ffc`5f21be38?????user32!SendMessageWorker+0x11e
02?0000008c`00ffed10?00007ffc`124fd4af?????user32!SendMessageW+0xf8
03?0000008c`00ffed70?00007ffc`125e943b?????cogxImagingDevice!DllUnregisterServer+0x3029f
04?0000008c`00ffeda0?00007ffc`125e9685?????cogxImagingDevice!DllUnregisterServer+0x11c22b
05?0000008c`00ffede0?00007ffc`600b50e7?????cogxImagingDevice!DllUnregisterServer+0x11c475
06?0000008c`00ffee20?00007ffc`60093ccd?????ntdll!LdrpCallInitRoutine+0x6f
07?0000008c`00ffee90?00007ffc`60092eef?????ntdll!LdrpProcessDetachNode+0xf5
08?0000008c`00ffef60?00007ffc`600ae319?????ntdll!LdrpUnloadNode+0x3f
09?0000008c`00ffefb0?00007ffc`600ae293?????ntdll!LdrpDecrementModuleLoadCountEx+0x71
0a?0000008c`00ffefe0?00007ffc`5cd7c00e?????ntdll!LdrUnloadDll+0x93
0b?0000008c`00fff010?00007ffc`5d47cf78?????KERNELBASE!FreeLibrary+0x1e
0c?0000008c`00fff040?00007ffc`5d447aa3?????combase!CClassCache::CDllPathEntry::CFinishObject::Finish+0x28?[onecore\com\combase\objact\dllcache.cxx?@?3420]?
0d?0000008c`00fff070?00007ffc`5d4471a9?????combase!CClassCache::CFinishComposite::Finish+0x4b?[onecore\com\combase\objact\dllcache.cxx?@?3530]?
0e?0000008c`00fff0a0?00007ffc`5d3f1499?????combase!CClassCache::FreeUnused+0xdd?[onecore\com\combase\objact\dllcache.cxx?@?6547]?
0f?0000008c`00fff650?00007ffc`5d3f13c7?????combase!CoFreeUnusedLibrariesEx+0x89?[onecore\com\combase\objact\dllapi.cxx?@?117]?
10?(Inline?Function)?--------`--------?????combase!CoFreeUnusedLibraries+0xa?[onecore\com\combase\objact\dllapi.cxx?@?74]?
11?0000008c`00fff690?00007ffc`6008a019?????combase!CDllHost::MTADllUnloadCallback+0x17?[onecore\com\combase\objact\dllhost.cxx?@?929]?
12?0000008c`00fff6c0?00007ffc`6008bec4?????ntdll!TppTimerpExecuteCallback+0xa9
13?0000008c`00fff710?00007ffc`5f167e94?????ntdll!TppWorkerThread+0x644
14?0000008c`00fffa00?00007ffc`600d7ad1?????kernel32!BaseThreadInitThunk+0x14
15?0000008c`00fffa30?00000000`00000000?????ntdll!RtlUserThreadStart+0x21

可以看到,cogxImagingDevice 發起了一個 user32!SendMessageW 同步方法,導致程序徹底死鎖,可能有些朋友有點懵,我簡單羅列下。

  1. 44 號線程使用 CreateThread 創建線程,但必須要先獲取 加載鎖,所以一直在等待 61 號線程釋放加載鎖。

  2. 61 號線程用同步的方式 user32!SendMessageW 給 主線程的 WndProc 網關函數打同步消息,等待 主線程給予響應,而此時主線程正在等待 GC 完成。

  3. 主線程 在無限期的 等待 GC 結束。

綜合來看,只要主線程不響應 44 號線程, 44號線程就不會釋放 加載鎖,這個 加載鎖 不釋放,就會導致很多的線程都無法初始化完畢,這個在它的 dump 中也反應出來了,代碼如下:

70??Id:?300.4f0?Suspend:?0?Teb:?0000008c`102e1000?Unfrozen#?Child-SP??????????RetAddr???????????????Call?Site
00?0000008c`0ecff388?00007ffc`6008902d?????ntdll!NtWaitForSingleObject+0x14
01?0000008c`0ecff390?00007ffc`600b29a7?????ntdll!LdrpDrainWorkQueue+0x15d
02?0000008c`0ecff3d0?00007ffc`600e76d5?????ntdll!LdrpInitializeThread+0x8b
03?0000008c`0ecff4b0?00007ffc`600e7633?????ntdll!_LdrpInitialize+0x89
04?0000008c`0ecff550?00007ffc`600e75de?????ntdll!LdrpInitialize+0x3b
05?0000008c`0ecff580?00000000`00000000?????ntdll!LdrInitializeThunk+0xe71??Id:?300.1c88?Suspend:?0?Teb:?0000008c`102e5000?Unfrozen#?Child-SP??????????RetAddr???????????????Call?Site
00?0000008c`0f4ff268?00007ffc`6008902d?????ntdll!NtWaitForSingleObject+0x14
01?0000008c`0f4ff270?00007ffc`600b29a7?????ntdll!LdrpDrainWorkQueue+0x15d
02?0000008c`0f4ff2b0?00007ffc`600e76d5?????ntdll!LdrpInitializeThread+0x8b
03?0000008c`0f4ff390?00007ffc`600e7633?????ntdll!_LdrpInitialize+0x89
04?0000008c`0f4ff430?00007ffc`600e75de?????ntdll!LdrpInitialize+0x3b
05?0000008c`0f4ff460?00000000`00000000?????ntdll!LdrInitializeThunk+0xe72??Id:?300.15c0?Suspend:?0?Teb:?0000008c`102e7000?Unfrozen#?Child-SP??????????RetAddr???????????????Call?Site
00?0000008c`0f8ff278?00007ffc`6008902d?????ntdll!NtWaitForSingleObject+0x14
01?0000008c`0f8ff280?00007ffc`600b29a7?????ntdll!LdrpDrainWorkQueue+0x15d
02?0000008c`0f8ff2c0?00007ffc`600e76d5?????ntdll!LdrpInitializeThread+0x8b
03?0000008c`0f8ff3a0?00007ffc`600e7633?????ntdll!_LdrpInitialize+0x89
04?0000008c`0f8ff440?00007ffc`600e75de?????ntdll!LdrpInitialize+0x3b
05?0000008c`0f8ff470?00000000`00000000?????ntdll!LdrInitializeThunk+0xe73??Id:?300.764?Suspend:?0?Teb:?0000008c`102ef000?Unfrozen#?Child-SP??????????RetAddr???????????????Call?Site
00?0000008c`0fcff388?00007ffc`6008902d?????ntdll!NtWaitForSingleObject+0x14
01?0000008c`0fcff390?00007ffc`600b29a7?????ntdll!LdrpDrainWorkQueue+0x15d
02?0000008c`0fcff3d0?00007ffc`600e76d5?????ntdll!LdrpInitializeThread+0x8b
03?0000008c`0fcff4b0?00007ffc`600e7633?????ntdll!_LdrpInitialize+0x89
04?0000008c`0fcff550?00007ffc`600e75de?????ntdll!LdrpInitialize+0x3b
05?0000008c`0fcff580?00000000`00000000?????ntdll!LdrInitializeThunk+0xe

可以看到,有很多的線程都卡死在 ntdll!LdrpInitializeThread+0x8b 處無法進行下去,那這個方法到底在做什么呢?可以看下 反匯編代碼

0:000>?u?ntdll!LdrpInitializeThread+0x8b
ntdll!LdrpInitializeThread+0x8b:
00007ffc`600b29a7?e874a50000??????call????ntdll!LdrpAcquireLoaderLock?(00007ffc`600bcf20)
00007ffc`600b29ac?90??????????????nop
00007ffc`600b29ad?488b1d1c2a1200??mov?????rbx,qword?ptr?[ntdll!PebLdr+0x10?(00007ffc`601d53d0)]
00007ffc`600b29b4?488d05152a1200??lea?????rax,[ntdll!PebLdr+0x10?(00007ffc`601d53d0)]
00007ffc`600b29bb?483bd8??????????cmp?????rbx,rax
00007ffc`600b29be?0f84c5000000????je??????ntdll!LdrpInitializeThread+0x16d?(00007ffc`600b2a89)
....

從匯編中可以清晰的看到,都卡在獲取加載鎖 ntdll!LdrpAcquireLoaderLock 函數上。

三:總結

這是一個由 cogxImagingDevice.dll引發的程序死鎖,查了下百度是一個商業版的 視覺圖像庫,對此我也無法解決,只能建議朋友。

  1. 熟悉下這個 dll 的配置,如果不是配置造成建議提交官方解決。

  2. 爭取做到 C# 和 C++ 的進程級隔離,或者干脆替換掉 cogxImagingDevice.dll ,雖然這個難度有點大。

這個 dump 給我們的教訓是:當 C# 和 C++ 混在一起,爭取做到最大可能的隔離,一旦出現問題非常考驗你對 windows 底層知識的理解,分析排錯門檻很高😂😂😂。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284050.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284050.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284050.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

BZOJ4573:[ZJOI2016]大森林——題解

http://www.lydsy.com/JudgeOnline/problem.php?id4573 https://www.luogu.org/problemnew/show/P3348#sub http://uoj.ac/problem/195 https://loj.ac/problem/2092 小Y家里有一個大森林&#xff0c;里面有n棵樹&#xff0c;編號從1到n。一開始這些樹都只是樹苗&#xff0c;只…

Spring中神奇@aotuWrited

好久沒有寫博客了&#xff0c;放假就是充電學習的時候&#xff0c;的確一直是這樣做的。來給自己一點掌聲。我們還是進入今天的主題吧。 我們自己寫代碼一般會向下面這樣干啊&#xff0c;因為這樣簡單&#xff0c;其余交給spring去做吧。Spring會自動把生成的userService注入進…

40個常用的springBoot注解

一、Spring Web MVC注解 RequestMapping RequestMapping注解的主要用途是將Web請求與請求處理類中的方法進行映射。 Spring MVC和Spring WebFlux都通過RquestMappingHandlerMapping和RequestMappingHndlerAdapter兩個類來提供對RequestMapping注解的支持。 RequestMapping注解…

.NET MAUI 跨平臺應用開發 I|.NET MAUI 跨平臺基礎

編輯&#xff1a;Alan Wang排版&#xff1a;Rani Sun微軟 Reactor 為幫助廣開發者&#xff0c;技術愛好者&#xff0c;更好的學習 .NET Core, C#, Python&#xff0c;數據科學&#xff0c;機器學習&#xff0c;AI&#xff0c;區塊鏈, IoT 等技術&#xff0c;將每周三到周六&…

走出宣傳,國產VR手機盒子到底哪家強?

國產VR手機盒子作為入門機是一個不錯的選擇&#xff0c;不過你知道哪一款更適合你嗎&#xff1f; 從去年看虛擬現實還是一個遙不可及的夢&#xff0c;今年卻真正的火起來了。各大廠商紛紛推出自家的VR設備&#xff0c;宣傳活動如火如荼。愛嘗鮮的你是否按耐不住? 如果你覺得動…

Shell 學習筆記之運算符

基本運算符 算術運算符 val expr 2 2 需要注意的是 表達式和運算符之間需要有空格&#xff08;比如2 2&#xff0c;不能是22&#xff09;兩邊最外面的字符是&#xff0c;在esc鍵下面&#xff0c;不是引號哦乘號* 前面必須加上反斜杠 \ 才能實現乘法效果&#xff0c;比如 exp…

POJ 2353 DP

雙向DP記錄路徑。 // by SiriusRen #include <stack> #include <cstdio> #include <cstring> using namespace std; stack<int>s; int n,m,RECL,RECR,minn0x3fffffff,a[555][555],f[555][555],recl[555][555],recr[555][555]; int main(){memset(f,0x3…

【ArcGIS Pro微課1000例】0024:自定義坐標系統---以阿爾伯斯投影(Albers)為例

在實際工作中,經常需要進行矢量數據或柵格數據的投影轉換工作,但有時候ArcGIS中恰恰沒有我們需要的坐標系,此時,就需要我們自定義坐標系。本文以阿爾伯斯投影(Albers)為例,講解自定義投影的一般過程及注意事項。 文章目錄 一、自定義坐標系二、投影轉換一、自定義坐標系…

Linux 操作必備 150 個命令

linux 命令是對 Linux 系統進行管理的命令。對于 Linux 系統來說&#xff0c;無論是中央處理器、內存、磁盤驅動器、鍵盤、鼠標&#xff0c;還是用戶等都是文件&#xff0c; Linux 系統管理的命令是它正常運行的核心&#xff0c;與之前的 DOS 命令類似。 linux 命令在系統中有兩…

dotnet 6 為什么網絡請求不跟隨系統網絡代理變化而動態切換代理

本文記錄在 dotnet 6 的網絡和在 .NET Framework 的行為的變更。在 dotnet 6 下&#xff0c;默認的網絡請求在系統網絡代理變更的時候&#xff0c;是不會動態切換代理的。例如在應用運行進行網絡通訊之后&#xff0c;打開 Fiddler 抓包&#xff0c;此時將會發現 Fiddler 抓不到…

舊金山參議員提議發布“封殺令”,理由是馬路不為機器人所服務

說實話&#xff0c;這個理由有夠奇葩。 因為快遞無人機所受限制頗多&#xff0c;漸漸地&#xff0c;越來越多的快遞機器人被研制出來&#xff08;這里的“機器人”&#xff0c;包括無人車和及機器人&#xff09;&#xff0c;用于城市的快遞發送&#xff0c;比如國內的京東無人…

Socket編程:之雙機通信

服務端&#xff1a; 1 #include<sys/socket.h>2 #include<sys/types.h>3 #include<stdio.h>4 #include<unistd.h>5 #include<stdlib.h>6 #include<string.h>7 #include<netdb.h>8 #include<netinet/in.h>9 #include<arpa/i…

jquery中$each()

$.each()&#xff1a;可用于遍歷任何的集合(無論是數組或對象) $(selector).each()&#xff1a;專用于jquery對象的遍歷, 如果是數組,回調函數每次傳入數組的索引和對應的值(值亦可以通過this 關鍵字獲取,但javascript總會包裝this 值作為一個對象—盡管是一個字符串或是一個數…

【QGIS入門實戰精品教程】7.2:QGIS點狀數據符號化設置案例教程

點狀符號化的類型有:單一符號、分類、漸進、基于規則、點的位移、點聚類、熱圖。 相關閱讀: 【QGIS入門實戰精品教程】7.1:QGIS面狀數據符號化設置案例教程 文章目錄 一、單一符號二、分類三、漸進四、基于規則五、點的位移六、點聚類七、熱圖一、單一符號 跟面狀符號一樣,…

SpringCloud與Dubbo的比較

Dubbo 一、dubbo簡介 Dubbo是阿里巴巴公司開源的一個高性能優秀的服務框架&#xff0c;使得應用可通過高性能的RPC實現服務的輸出和輸入功能&#xff0c;可以和Spring框架無縫集成。 Dubbo是一款高性能、輕量級的開源Java RPC框架&#xff0c;它提供了三大核心能力&#xff…

VR 技術加上 8K 畫質! 2016 年里約奧運會亮點十足

據報道&#xff0c;2016 年里約奧運會將運用到 VR 技術。 最近&#xff0c;奧林匹克廣播服務公司&#xff08;OBS&#xff09;表示出對虛擬現實技術的興趣&#xff0c;其實用虛擬現實技術報道賽事已經不是什么新鮮的事了&#xff0c;之前 NBA 就這樣做過&#xff0c;但是將奧運…

POJ 1986 Distance Queries(LCA)

【題目鏈接】 http://poj.org/problem?id1986 【題目大意】 給出一棵樹&#xff0c;問任意兩點間距離。 【題解】 u,v之間距離為dis[u]dis[v]-2*dis[LCA(u,v)] 【代碼】 #include <cstdio> #include <algorithm> #include <cstring> using namespace std; c…

WPF 實現柱形統計圖

WPF 實現柱形統計圖WPF 實現柱形統計圖作者&#xff1a;WPFDevelopersOrg原文鏈接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40&#xff1b;Visual Studio 2022;項目使用 MIT 開源許可協議&#xff1b;避免畫線發虛DrawingContext…

Win11卸載WSL,卸載Windows子系統

雖然 Linux 發行版可以通過 Microsoft Store 安裝&#xff0c;但不能通過 Microsoft Store 卸載。 可以通過下列命令卸載。 1、查看當前環境安裝的wsl wsl --list2、注銷&#xff08;卸載&#xff09;當前安裝的Linux的Windows子系統 wsl --unregister Ubuntu3、卸載成功&#…

100億人口會挨餓嗎?人工智能迎擊全球糧食問題

給作物看病的AI、走路“長眼”的拖拉機、上帝視角的衛星數據分析——未來吃飯就靠它們了。 圖片來源&#xff1a;Blue River Technology 人類又面臨了一項危機——隨著人口不斷膨脹&#xff0c;到2050年人類總人口也許要達到100億&#xff0c;然而&#xff0c;地球卻沒有等比例…