Artikel ini adalah artikel cermin dari terjemahan mesin, silakan klik di sini untuk melompat ke artikel aslinya.

Melihat: 6277|Jawab: 2

[Komunikasi] [Putar] Kalimat Task.Result akan dikunci, bagaimana lagi cara menulis kode ini?

[Salin tautan]
Dipaparkan pada 2022-8-7 21:11:58 | | | |
1: Latar belakang

1. Bercerita

Beberapa hari yang lalu, ketika saya mengindeks artikel tentang .NET Advanced Debugging ke github, saya menemukan komentar yang menarik, lihat artikel untuk detailnya, tangkapan layarnya adalah sebagai berikut:



Ini mungkin berarti bahwa mengeksekusi Task.Result di bawah utas utama Winform akan menyebabkan kebuntuan, saya juga melihat tautan referensi dalam gambar, Stephen adalah bos mutlak, tetapi artikel ini terutama tentang indoktrinasi paragraf teks yang besar, dan itu tidak benar-benar membiarkan Anda melihat apa yang Anda lihat, jadi saya akan menganalisisnya dari perspektif windbg.

2: Analisis Windbg

1. Apakah itu benar-benar menemui jalan buntu?

Tentu saja, saya belum bermain winform selama bertahun-tahun, dan saya tidak tahu apakah itu akan terjadi, setidaknya tidak di konsol.

Kodenya sangat sederhana, jalankan program, klik klik, dan benar saja, antarmukanya macet, yang agak luar biasa.

2. Cari penyebab kebuntuan

Selanjutnya, buruan dan lampirkan windbg ke dalam prosesnya untuk mengetahuinya.

1) Lihat utas utama
Antarmukanya tidak responsif, jadi tentu saja utas utama macet, jadi Anda perlu melihat apa yang dilakukan utas utama saat ini. Gunakan perintah ~0s + !clrstack.

0:000> !clrstack
ID Utas OS: 0x5a10 (0)
        Situs Panggilan IP SP Anak
0000004d10dfde00 00007ffb889a10e4 [GCFrame: 0000004d10dfde00]
0000004d10dfdf28 00007ffb889a10e4 [HelperMethodFrame_1OBJ: 0000004d10dfdf28] System.Threading.Monitor.ObjWait(Boolean, Int32, System.Object)
0000004d10dfe040 00007ffb66920d64 System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken)
0000004d10dfe0d0 00007ffb6691b4bb System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken)
0000004d10dfe140 00007ffb672601d1 System.Threading.Tasks.Task.InternalWait(Int32, System.Threading.CancellationToken)
0000004d10dfe210 00007ffb6725cfa7 System.Threading.Tasks.Task'1[[System.__Canon, mscorlib]]. GetResultCore(Boolean)
0000004d10dfe250 00007ffb18172a1b WindowsFormsApp4.Form1.button1_Click(System.Object, System.EventArgs) [E:\net5\ConsoleApp1\WindowsFormsApp4\Form1.cs @ 26]
0000004d10dfe2b0 00007ffb3a024747 System.Windows.Forms.Control.OnClick(System.EventArgs)
0000004d10dfe2f0 00007ffb3a027b83 System.Windows.Forms.Button.OnClick(System.EventArgs)
0000004d10dfe340 00007ffb3a837231 System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs)
0000004d10dfe400 00007ffb3a7e097d System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message ByRef, System.Windows.Forms.MouseButtons, Int32)
0000004d10dfe480 00007ffb3a0311cc System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
0000004d10dfe540 00007ffb3a0b0c97 System.Windows.Forms.ButtonBase.WndProc(System.Windows.Forms.Message ByRef)
0000004d10dfe5c0 00007ffb3a0b0be5 System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef)
0000004d10dfe5f0 00007ffb3a030082 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
0000004d10dfe690 00007ffb3a765a02 DomainBoundILStubClass.IL_STUB_ReversePInvoke(int64, int32, int64, int64)
0000004d10dfe9d0 00007ffb776d221e [InlinedCallFrame: 0000004d10dfe9d0] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)
0000004d10dfe9d0 00007ffb3a0b9489 [InlinedCallFrame: 0000004d10dfe9d0] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)
0000004d10dfe9a0 00007ffb3a0b9489 DomainBoundILStubClass.IL_STUB_PInvoke(MSG ByRef)
0000004d10dfea60 00007ffb3a046661 System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop( IntPtr, Int32, Int32)
0000004d10dfeb50 00007ffb3a045fc7 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
0000004d10dfebf0 00007ffb3a045dc2 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
0000004d10dfec50 00007ffb181708e2 WindowsFormsApp4.Program.Main() [E:\net5\ConsoleApp1\WindowsFormsApp4\Program.cs @ 19]
0000004d10dfee78 00007ffb776d6923 [GCFrame: 0000004d10dfee78]
Dari output tumpukan, utas utama akhirnya macet di Monitor.ObjWait di bawah Task.Result, yang berarti belum mengambil jsonString terakhir, yang sangat aneh, sudah beberapa menit, apakah ada masalah dengan jaringan? Jaring saya 100M penuh dengan daya tembak...

2) Ke mana jsonString pergi?
Jika jsonString ditemukan pada tumpukan terkelola, itu berarti ada sesuatu pada program yang membuat hasil tertunda, gunakan perintah !dumpheap -type String -min 8500 + !do 000001f19002fcf0 untuk melihat, seperti yang ditunjukkan pada gambar di bawah ini:



Dari gambar tersebut, dapat dilihat dengan jelas bahwa html kembali, karena semuanya kembali, mengapa Task.Result belum berakhir? Langkah selanjutnya adalah melihat siapa yang memegang html ini dan menggunakan !gcroot.

0:000> !gcroot 000001f19002fcf0
Benang 5a10:
    0000004d10dfe250 00007ffb18172a1b WindowsFormsApp4.Form1.button1_Click(System.Object, System.EventArgs) [E:\net5\ConsoleApp1\WindowsFormsApp4\Form1.cs @ 26]
        RBP+10: 0000004D10DFE2B0
            -> 000001f180007f78 WindowsFormsApp4.Form1
            -> 000001f180070d68 System.ComponentModel.EventHandlerList
            -> 000001f180071718 System.ComponentModel.EventHandlerList+ListEntry
            -> 000001f1800716d8 Sistem.Penanganan Peristiwa
            -> 000001f1800716b0 System.Windows.Forms.ApplicationContext
            -> 000001f180071780 Sistem.Penanganan Peristiwa
            -> 000001f18006ab38 System.Windows.Forms.Application+ThreadContext
            -> 000001f18006b140 System.Windows.Forms.Application+MarshalingControl
            -> 000001f18016c9c8 System.Collections.Queue
            -> 000001f18016ca00 Sistem.Objek[]
            -> 000001f18016c948 System.Windows.Forms.Control+ThreadMethodEntry
            -> 000001f18016c8b8 Sistem.Objek[]
            -> 000001f1800e6f80 Sistem.Tindakan
            -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner
            -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2
            -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]]
            -> 000001f19002fcf0 Sistem.String

Menemukan 1 akar unik (lari '! GCRoot -all' untuk melihat semua akar).
Dari hasil output, System.String ini akhirnya dipegang oleh WindowsFormsApp4.Form1 dari utas 5a10, dan Anda dapat menggunakan !t untuk memverifikasi apa itu utas 5a10.

0:000> !t Kunci  
       ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
   0 1 5a10 000001f1f1b01200 2026020 preemptive 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA
   2 2 712c 000001f1f1b2a270 2b220 Preemptive 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Saya pergi, 5a10 ternyata adalah utas utama, itu benar-benar agak membingungkan, utas utama macet, dan senar dipegang oleh utas utama, sama sekali tidak bisa dijelaskan.

3) Cari titik terobosan
Masih kembali dan memikirkan rantai referensi ini dengan tenang, saya menemukan bahwa ada Antrian di sini: -> 000001f18016c9c8 System.Collections.Queue, saya punya ide, saya dapat men-debug kode sumber dengan menempatkan titik henti di Antrean, dan alat ini menggunakan DnSpy, lakukan saja.



Seperti yang Anda lihat dari gambar, saat memasuki Queue, thread 10 digunakan, yang berarti string belum dipegang oleh thread utama saat ini.



Dari diagram tersebut, dapat dilihat bahwa tugas berkelanjutan akhirnya dijadwalkan oleh WindowsFormsSynchronizationContext.Post ke dalam Queue under Control, dan data dalam Queue ini perlu dieksekusi oleh thread UI, jadi ada dialog sebagai berikut:

Utas utama: Tugas saudara, kapan Anda akan selesai mengeksekusinya? Saya menunggu Anda menyelesaikan sinyal?

tugas:Saudaraku, jika kamu tidak mengeksekusi aku, bagaimana aku bisa menyelesaikannya?

Benang utama:Oh...

Singkatnya: tugas kelanjutan telah mencapai antrian menunggu utas utama dieksekusi, dan saat ini, utas utama tertegun dan telah menunggu tugas kelanjutan selesai=true, masalahnya ada di sini: bagaimana tugas kelanjutan tidak dapat dieksekusi Complete=true? Jadi mereka seperti ini.

Tiga: Cara memecahkannya

Mengetahui sebab dan akibatnya, metode retak ini sederhana, secara kasar dibagi menjadi dua jenis.

1. Dilarang menjatuhkan tugas lanjutan ke dalam antrian

Untuk memotong jalur ini, implikasinya adalah membiarkan kumpulan utas mengakhiri tugas dengan sendirinya, sehingga utas UI dapat merasakan bahwa tugas telah selesai, dan akhirnya utas UI dapat mendapatkan html akhir, yaitu menambahkan ConfigureAwait(false) setelah menunggu, sebagai berikut:



2. Blokir utas utama

Jika utas utama tidak diblokir, maka utas utama dapat dengan bebas mendapatkan tugas yang perlu dijalankan di Control.Queue, dan metodenya sangat sederhana, cukup tambahkan tunggu sebelum GetJsonAsync.



Tiga: Ringkasan

Kesimpulannya adalah melakukan lebih banyak praktik praktis Anda sendiri, pengetahuan teoretis secara paksa ditanamkan dalam diri Anda oleh orang lain, apakah itu benar atau salah, pada kenyataannya, Anda tidak memiliki dasar di hati Anda, verifikasi praktis adalah apa yang sebenarnya menjadi milik Anda, dan sulit untuk dilupakan, bagaimanapun juga, Anda benar-benar mengalami, berlatih, dan memverifikasi.

Asli:Login hyperlink terlihat.





Mantan:Docker memodifikasi dan membatasi CPU kontainer, memori, dan sumber daya lainnya
Depan:Array JS adalah perbedaan dan penggunaan dari setiap dan beberapa
Dipaparkan pada 2022-8-8 17:01:32 |
Belajar untuk belajar...
Dipaparkan pada 2022-9-2 11:39:19 |
Belajar sedikit
Sanggahan:
Semua perangkat lunak, materi pemrograman, atau artikel yang diterbitkan oleh Code Farmer Network hanya untuk tujuan pembelajaran dan penelitian; Konten di atas tidak boleh digunakan untuk tujuan komersial atau ilegal, jika tidak, pengguna akan menanggung semua konsekuensi. Informasi di situs ini berasal dari Internet, dan sengketa hak cipta tidak ada hubungannya dengan situs ini. Anda harus sepenuhnya menghapus konten di atas dari komputer Anda dalam waktu 24 jam setelah pengunduhan. Jika Anda menyukai program ini, harap dukung perangkat lunak asli, pembelian pendaftaran, dan dapatkan layanan asli yang lebih baik. Jika ada pelanggaran, silakan hubungi kami melalui email.

Mail To:help@itsvse.com