Dalam buku Programming C #, ada beberapa kode contoh tentang SynchronizationContext
:
SynchronizationContext originalContext = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(delegate {
string text = File.ReadAllText(@"c:\temp\log.txt");
originalContext.Post(delegate {
myTextBox.Text = text;
}, null);
});
Saya pemula di utas, jadi harap jawab dengan detail. Pertama, saya tidak tahu apa artinya konteks, apa yang disimpan dalam program originalContext
? Dan ketika Post
metode ini diaktifkan, apa yang akan dilakukan thread UI?
Jika saya menanyakan beberapa hal konyol, tolong perbaiki saya, terima kasih!
EDIT: Misalnya, bagaimana jika saya hanya menulis myTextBox.Text = text;
dalam metode ini, apa bedanya?
c#
.net
multithreading
FannyFan
sumber
sumber
async
Sayaawait
mengandalkan diSynchronizationContext
bawahnya.Jawaban:
Sederhananya,
SynchronizationContext
merupakan lokasi "di mana" kode dapat dieksekusi. Delegasi yang diteruskan ke metodeSend
atauPost
metode akan dipanggil di lokasi itu. (Post
adalah versi non-blocking / asinkron dariSend
.)Setiap utas dapat memiliki
SynchronizationContext
instance yang terkait dengannya. Thread yang sedang berjalan dapat dikaitkan dengan konteks sinkronisasi dengan memanggil metode statisSynchronizationContext.SetSynchronizationContext
, dan konteks saat ini dari thread yang sedang berjalan dapat ditanyakan melaluiSynchronizationContext.Current
properti .Terlepas dari apa yang baru saja saya tulis (setiap utas memiliki konteks sinkronisasi terkait),
SynchronizationContext
tidak selalu mewakili utas tertentu ; itu juga dapat meneruskan permintaan delegasi yang diteruskan ke salah satu dari beberapa utas (misalnya keThreadPool
utas pekerja), atau (setidaknya secara teori) ke inti CPU tertentu , atau bahkan ke host jaringan lain . Di mana delegasi Anda berakhir tergantung pada jenis yangSynchronizationContext
digunakan.Formulir Windows akan menginstal
WindowsFormsSynchronizationContext
pada utas di mana formulir pertama dibuat. (Utas ini biasa disebut "utas UI".) Jenis konteks sinkronisasi ini memanggil para delegasi yang dilewatinya pada utas tersebut. Ini sangat berguna karena Formulir Windows, seperti banyak kerangka kerja UI lainnya, hanya memungkinkan manipulasi kontrol pada utas yang sama dengan yang mereka buat.Kode yang telah Anda lewati
ThreadPool.QueueUserWorkItem
akan dijalankan pada utas pekerja rangkaian utas. Artinya, itu tidak akan dieksekusi pada utas tempat AndamyTextBox
dibuat, jadi Formulir Windows cepat atau lambat (terutama dalam rilis Rilis) melemparkan pengecualian, memberi tahu Anda bahwa Anda tidak dapat mengaksesmyTextBox
dari seluruh utas lainnya.Inilah sebabnya mengapa Anda harus "beralih kembali" dari utas pekerja ke "utas UI" (tempat
myTextBox
dibuat) sebelum penugasan tertentu. Ini dilakukan sebagai berikut:Saat Anda masih di utas UI, tangkap Windows Forms 'di
SynchronizationContext
sana, dan simpan referensi ke sana dalam variabel (originalContext
) untuk digunakan nanti. Anda harus memintaSynchronizationContext.Current
pada titik ini; jika Anda menanyakannya di dalam kode yang diteruskan keThreadPool.QueueUserWorkItem
, Anda mungkin mendapatkan konteks sinkronisasi apa pun yang dikaitkan dengan utas pekerja rangkaian utas. Setelah Anda menyimpan referensi ke konteks Formulir Windows, Anda dapat menggunakannya di mana saja dan kapan saja untuk "mengirim" kode ke utas UI.Setiap kali Anda perlu memanipulasi elemen UI (tetapi tidak, atau mungkin tidak, pada utas UI lagi), mengakses konteks sinkronisasi Formulir Windows melalui
originalContext
, dan menyerahkan kode yang akan memanipulasi UI untuk salah satuSend
atauPost
.Komentar dan petunjuk terakhir:
Konteks sinkronisasi apa yang tidak akan dilakukan untuk Anda memberi tahu Anda kode mana yang harus dijalankan di lokasi / konteks tertentu, dan kode mana yang hanya dapat dieksekusi secara normal, tanpa meneruskannya ke a
SynchronizationContext
. Untuk memutuskan itu, Anda harus mengetahui aturan dan persyaratan kerangka kerja yang Anda pemrogramankan - Formulir Windows dalam kasus ini.Jadi, ingat aturan sederhana ini untuk Formulir Windows: JANGAN mengakses kontrol atau formulir dari utas selain dari yang membuatnya. Jika Anda harus melakukan ini, gunakan
SynchronizationContext
mekanisme seperti dijelaskan di atas, atauControl.BeginInvoke
(yang merupakan cara khusus Windows Forms untuk melakukan hal yang persis sama).Jika Anda pemrograman sedang melawan NET 4.5 atau lambat, Anda dapat membuat hidup Anda lebih mudah dengan mengkonversi kode Anda yang secara eksplisit menggunakan
SynchronizationContext
,ThreadPool.QueueUserWorkItem
,control.BeginInvoke
, dll ke baruasync
/await
kata kunci dan Paralel Tugas Perpustakaan (TPL) , yaitu API sekitarnya yangTask
danTask<TResult>
kelas. Ini akan, pada tingkat yang sangat tinggi, menjaga menangkap konteks sinkronisasi utas UI, memulai operasi asinkron, kemudian kembali ke utas UI sehingga Anda dapat memproses hasil operasi.sumber
Application.Run
, IIRC). Ini adalah topik yang cukup maju dan bukan sesuatu yang dilakukan dengan santai.null
) atau turunanSynchronizationContext
(atau subkelasnya). Inti dari kutipan itu bukanlah apa yang Anda dapatkan, tetapi apa yang tidak akan Anda dapatkan: konteks sinkronisasi utas UI.Saya ingin menambahkan jawaban lain,
SynchronizationContext.Post
cukup mengantre callback untuk eksekusi selanjutnya pada utas target (biasanya selama siklus berikutnya dari loop pesan utas target), dan kemudian eksekusi berlanjut pada utas panggilan. Di sisi lain,SynchronizationContext.Send
mencoba untuk mengeksekusi callback pada utas target segera, yang memblokir utas panggilan dan dapat mengakibatkan kebuntuan. Dalam kedua kasus, ada kemungkinan untuk reentrancy kode (memasukkan metode kelas pada utas eksekusi yang sama sebelum panggilan sebelumnya ke metode yang sama telah kembali).Jika Anda akrab dengan model pemrograman Win32, analogi sangat dekat akan
PostMessage
danSendMessage
API, yang dapat Anda hubungi untuk mengirimkan pesan dari berbagai benang dari satu jendela target.Berikut adalah penjelasan yang sangat bagus tentang konteks sinkronisasi itu: Ini Semua Tentang Konteks Sinkronisasi .
sumber
Ini menyimpan penyedia sinkronisasi, kelas yang berasal dari SynchronizationContext. Dalam hal ini yang mungkin akan menjadi turunan dari WindowsFormsSynchronizationContext. Kelas itu menggunakan metode Control.Invoke () dan Control.BeginInvoke () untuk mengimplementasikan metode Send () dan Post (). Atau bisa juga DispatcherSynchronizationContext, ia menggunakan Dispatcher.Invoke () dan BeginInvoke (). Dalam aplikasi WinForms atau WPF, penyedia itu secara otomatis diinstal segera setelah Anda membuat jendela.
Saat Anda menjalankan kode pada utas lain, seperti utas kumpulan utas yang digunakan dalam cuplikan, maka Anda harus berhati-hati agar Anda tidak langsung menggunakan objek yang tidak aman-utas. Seperti objek antarmuka pengguna apa pun, Anda harus memperbarui properti TextBox.Text dari utas yang membuat TextBox. Metode Post () memastikan bahwa target delegasi berjalan pada utas itu.
Berhati-hatilah bahwa cuplikan ini agak berbahaya, hanya akan berfungsi dengan benar saat Anda memanggilnya dari utas UI. SynchronizationContext.Current memiliki nilai yang berbeda di utas yang berbeda. Hanya utas UI yang memiliki nilai yang dapat digunakan. Dan adalah alasan kode harus menyalinnya. Cara yang lebih mudah dibaca dan lebih aman untuk melakukannya, di aplikasi Winforms:
Yang memiliki kelebihan itu berfungsi ketika dipanggil dari utas apa pun . Keuntungan menggunakan SynchronizationContext.Current adalah bahwa ia masih berfungsi apakah kode tersebut digunakan dalam Winforms atau WPF, itu penting di perpustakaan. Ini tentu bukan contoh yang baik dari kode semacam itu, Anda selalu tahu apa jenis TextBox yang Anda miliki di sini sehingga Anda selalu tahu apakah akan menggunakan Control.BeginInvoke atau Dispatcher.BeginInvoke. Sebenarnya menggunakan SynchronizationContext.Current tidak umum.
Buku ini mencoba mengajarkan Anda tentang threading, jadi menggunakan contoh cacat ini tidak masalah. Dalam kehidupan nyata, dalam beberapa kasus di mana Anda mungkin mempertimbangkan untuk menggunakan SynchronizationContext.Current, Anda masih akan menyerahkannya ke async / menunggu kata kunci C # atau TaskScheduler.FromCurrentSynchronizationContext () untuk melakukannya untuk Anda. Tetapi harap dicatat bahwa mereka masih melakukan kesalahan seperti yang dilakukan cuplikan ketika Anda menggunakannya di utas yang salah, untuk alasan yang sama persis. Pertanyaan yang sangat umum di sekitar sini, tingkat abstraksi ekstra berguna tetapi membuatnya lebih sulit untuk mencari tahu mengapa mereka tidak bekerja dengan benar. Semoga buku ini juga memberi tahu Anda kapan tidak menggunakannya :)
sumber
Tujuan dari konteks sinkronisasi di sini adalah untuk memastikan bahwa
myTextbox.Text = text;
dipanggil pada utas UI utama.Windows mengharuskan kontrol GUI diakses hanya oleh utas yang dibuatnya. Jika Anda mencoba menetapkan teks di utas latar tanpa terlebih dahulu menyinkronkan (melalui salah satu dari beberapa cara, seperti ini atau pola Invoke) maka pengecualian akan dibuang.
Apa yang dilakukan adalah menyimpan konteks sinkronisasi sebelum membuat utas latar belakang, lalu utas latar belakang menggunakan konteks. Metode posting menjalankan kode GUI.
Ya, kode yang Anda tunjukkan pada dasarnya tidak berguna. Mengapa membuat utas latar belakang, hanya untuk segera perlu kembali ke utas UI utama? Itu hanya sebuah contoh.
sumber
Ke Sumber
Misalnya: Misalkan Anda memiliki dua utas, Utas 1 dan Utas 2. Katakan, Thread1 melakukan beberapa pekerjaan, dan kemudian Thread1 ingin mengeksekusi kode di Thread2. Salah satu cara yang mungkin untuk melakukannya adalah dengan meminta Thread2 untuk objek SynchronizationContext-nya, memberikannya kepada Thread1, dan kemudian Thread1 dapat memanggil SynchronizationContext. Kirim untuk mengeksekusi kode pada Thread2.
sumber
SynchronizationContext memberi kami cara untuk memperbarui UI dari utas yang berbeda (secara sinkron melalui metode Kirim atau secara tidak sinkron melalui metode Posting).
Lihatlah contoh berikut:
SynchronizationContext.Current akan mengembalikan konteks sinkronisasi utas UI. Bagaimana saya tahu ini? Di awal setiap formulir atau aplikasi WPF, konteksnya akan ditetapkan pada utas UI. Jika Anda membuat aplikasi WPF dan menjalankan contoh saya, Anda akan melihat bahwa ketika Anda mengklik tombol, itu tidur selama sekitar 1 detik, maka itu akan menampilkan konten file. Anda mungkin berharap itu tidak akan terjadi karena pemanggil metode UpdateTextBox (yang merupakan Work1) adalah metode yang diteruskan ke Thread, oleh karena itu ia harus menggunakan utas tersebut bukan utas UI utama, NOPE! Meskipun metode Work1 diteruskan ke utas, perhatikan bahwa ia juga menerima objek yang merupakan SyncContext. Jika Anda melihatnya, Anda akan melihat bahwa metode UpdateTextBox dijalankan melalui metode syncContext.Post dan bukan metode Work1. Lihatlah yang berikut ini:
Contoh terakhir dan yang ini menjalankan hal yang sama. Keduanya tidak memblokir UI saat itu berfungsi.
Sebagai kesimpulan, pikirkan SynchronizationContext sebagai utas. Ini bukan utas, ini menentukan utas (Perhatikan bahwa tidak semua utas memiliki SyncContext). Setiap kali kita memanggil metode Posting atau Kirim untuk memperbarui UI, itu seperti memperbarui UI secara normal dari utas UI utama. Jika, karena alasan tertentu, Anda perlu memperbarui UI dari utas yang berbeda, pastikan utas itu memiliki SyncContext utas utama UI dan panggil metode Kirim atau Posting di atasnya dengan metode yang ingin Anda jalankan dan Anda semua set.
Semoga ini bisa membantu Anda, sobat!
sumber
SynchronizationContext pada dasarnya adalah penyedia eksekusi delegasi callback yang terutama bertanggung jawab untuk memastikan bahwa delegasi dijalankan dalam konteks eksekusi yang diberikan setelah bagian kode tertentu (dimasukkan dalam tugas Task .Net TPL) dari suatu program telah menyelesaikan pelaksanaannya.
Dari sudut pandang teknis, SC adalah kelas C # sederhana yang berorientasi untuk mendukung dan menyediakan fungsinya secara khusus untuk objek Pustaka Tugas Paralel.
Setiap aplikasi .Net, kecuali untuk aplikasi konsol, memiliki implementasi khusus dari kelas ini berdasarkan kerangka dasar tertentu, yaitu: WPF, WindowsForm, Asp Net, Silverlight, ecc ..
Pentingnya objek ini terikat pada sinkronisasi antara hasil yang kembali dari eksekusi kode asyncronous dan pelaksanaan kode dependen yang menunggu hasil dari pekerjaan asyncronous itu.
Dan kata "konteks" adalah singkatan dari konteks eksekusi, yaitu konteks eksekusi saat ini di mana kode tunggu akan dieksekusi, yaitu sinkronisasi antara kode async dan kode tunggunya terjadi dalam konteks eksekusi tertentu, sehingga objek ini dinamai SynchronizationContext: itu mewakili konteks eksekusi yang akan menjaga sinkronisasi kode async dan eksekusi kode tunggu .
sumber
Contoh ini dari contoh Linqpad dari Joseph Albahari tetapi sangat membantu dalam memahami apa yang dilakukan konteks Sinkronisasi.
sumber