Saya punya skenario. (Formulir Windows, C #, .NET)
- Ada bentuk utama yang menampung beberapa kontrol pengguna.
- Kontrol pengguna melakukan beberapa operasi data berat, sehingga jika saya langsung memanggil
UserControl_Load
metode, UI menjadi tidak responsif selama durasi untuk eksekusi metode beban. - Untuk mengatasi ini saya memuat data pada utas yang berbeda (mencoba mengubah kode yang ada sesedikit mungkin)
- Saya menggunakan utas pekerja latar belakang yang akan memuat data dan ketika selesai akan memberi tahu aplikasi bahwa ia telah melakukan tugasnya.
- Sekarang muncul masalah nyata. Semua UI (bentuk utama dan kontrol pengguna anak) dibuat pada utas utama utama. Dalam metode LOAD dari usercontrol saya mengambil data berdasarkan nilai-nilai beberapa kontrol (seperti kotak teks) pada userControl.
Kodesemu akan terlihat seperti ini:
KODE 1
UserContrl1_LoadDataMethod()
{
if (textbox1.text == "MyName") // This gives exception
{
//Load data corresponding to "MyName".
//Populate a globale variable List<string> which will be binded to grid at some later stage.
}
}
Pengecualian yang diberikannya adalah
Operasi lintas-thread tidak valid: Kontrol diakses dari utas selain utas yang dibuatnya.
Untuk mengetahui lebih banyak tentang ini, saya melakukan beberapa googling dan sebuah saran muncul seperti menggunakan kode berikut
KODE 2
UserContrl1_LoadDataMethod()
{
if (InvokeRequired) // Line #1
{
this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
return;
}
if (textbox1.text == "MyName") // Now it wont give an exception
{
//Load data correspondin to "MyName"
//Populate a globale variable List<string> which will be binded to grid at some later stage
}
}
TAPI TAPI TAPI ... sepertinya saya kembali ke titik awal. Aplikasi kembali menjadi tidak responsif. Tampaknya karena eksekusi baris # 1 jika kondisi. Tugas pemuatan sekali lagi dilakukan oleh utas induk dan bukan yang ketiga yang saya hasilkan.
Saya tidak tahu apakah saya menganggap ini benar atau salah. Saya baru mengenal threading.
Bagaimana cara mengatasi ini dan juga apa efek dari eksekusi Baris # 1 jika diblokir?
Situasinya adalah ini : Saya ingin memuat data ke variabel global berdasarkan nilai kontrol. Saya tidak ingin mengubah nilai kontrol dari utas anak. Saya tidak akan pernah melakukannya dari utas anak.
Jadi hanya mengakses nilai sehingga data yang sesuai dapat diambil dari basis data.
sumber
Jawaban:
Sesuai komentar pembaruan Prerak K (sejak dihapus):
Solusi yang Anda inginkan akan terlihat seperti:
Lakukan pemrosesan serius Anda di utas terpisah sebelum Anda mencoba untuk beralih kembali ke utas kontrol. Sebagai contoh:
sumber
Model Threading di UI
Baca Model Threading di aplikasi UI ( tautan VB lama ada di sini ) untuk memahami konsep dasar. Tautan menavigasi ke halaman yang menjelaskan model threading WPF. Namun, Formulir Windows menggunakan ide yang sama.
Utas UI
Metode BeginInvoke dan Invoke
Memohon
MulaiInvoke
Solusi kode
Baca jawaban atas pertanyaan Bagaimana cara memperbarui GUI dari utas lain di C #? . Untuk C # 5.0 dan .NET 4.5 solusi yang disarankan ada di sini .
sumber
Anda hanya ingin menggunakan
Invoke
atauBeginInvoke
untuk pekerjaan minimum yang diperlukan untuk mengubah UI. Metode "berat" Anda harus dijalankan pada utas lain (misalnya viaBackgroundWorker
) tetapi kemudian menggunakanControl.Invoke
/Control.BeginInvoke
hanya untuk memperbarui UI. Dengan begitu utas UI Anda akan bebas menangani acara UI, dll.Lihat artikel threading saya untuk contoh WinForms - meskipun artikel itu ditulis sebelum
BackgroundWorker
tiba di tempat kejadian, dan saya khawatir saya belum memperbaruinya dalam hal itu.BackgroundWorker
hanya menyederhanakan panggilan balik sedikit.sumber
Saya tahu sudah terlambat sekarang. Namun bahkan hari ini jika Anda mengalami kesulitan mengakses kontrol lintas thread? Ini adalah jawaban terpendek hingga tanggal: P
Ini adalah cara saya mengakses kontrol formulir apa pun dari utas.
sumber
Invoke or BeginInvoke cannot be called on a control until the window handle has been created
. Saya menyelesaikannya di siniSaya mengalami masalah ini dengan
FileSystemWatcher
dan menemukan bahwa kode berikut menyelesaikan masalah:fsw.SynchronizingObject = this
Kontrol kemudian menggunakan objek formulir saat ini untuk menangani peristiwa, dan karenanya akan berada di utas yang sama.
sumber
.SynchronizingObject = Me
Saya menemukan kode check-and-invoke yang perlu dikotori di dalam semua metode yang terkait dengan formulir terlalu verbose dan tidak dibutuhkan. Berikut adalah metode ekstensi sederhana yang memungkinkan Anda melakukannya sepenuhnya:
Dan kemudian Anda bisa melakukan ini:
Tidak ada lagi main-main - sederhana.
sumber
t
dalam kasus ini akan menjaditextbox1
- ini disahkan sebagai argumenKontrol di .NET umumnya tidak aman untuk thread. Itu berarti Anda tidak boleh mengakses kontrol dari utas selain dari tempat ia tinggal. Untuk menyiasatinya, Anda perlu mengaktifkan kontrol, yang merupakan upaya sampel kedua Anda.
Namun, dalam kasus Anda semua yang Anda lakukan adalah melewati metode lama berjalan kembali ke utas utama. Tentu saja, itu bukan yang ingin Anda lakukan. Anda perlu memikirkan kembali ini sedikit sehingga semua yang Anda lakukan pada utas utama adalah menetapkan properti cepat di sana-sini.
sumber
Solusi terbersih (dan tepat) untuk masalah lintas-UI UI adalah dengan menggunakan SynchronizationContext, lihat Menyinkronkan panggilan ke UI dalam artikel aplikasi multi-utas , itu menjelaskannya dengan sangat baik.
sumber
Tampilan baru menggunakan Async / Menunggu dan panggilan balik. Anda hanya perlu satu baris kode jika Anda menyimpan metode ekstensi di proyek Anda.
Anda dapat menambahkan hal-hal lain ke metode Ekstensi seperti membungkusnya dalam pernyataan Coba / Tangkap, memungkinkan penelepon untuk memberitahukan jenis apa yang harus dikembalikan setelah selesai, pengecualian panggilan balik ke penelepon:
Menambahkan Try Catch, Logging Pengecualian Otomatis dan CallBack
sumber
Ini bukan cara yang disarankan untuk mengatasi kesalahan ini tetapi Anda dapat menekannya dengan cepat, ini akan melakukan pekerjaan. Saya lebih suka ini untuk prototipe atau demo. Menambahkan
dalam
Form1()
konstruktor.sumber
Ikuti cara paling sederhana (menurut saya) untuk memodifikasi objek dari utas lain:
sumber
Anda perlu melihat contoh Backgroundworker:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx Terutama cara berinteraksi dengan lapisan UI. Berdasarkan posting Anda, ini tampaknya menjawab masalah Anda.
sumber
Saya menemukan kebutuhan untuk ini ketika memprogram pengontrol aplikasi iOS-Phone monotouch di proyek prototipe winforms studio visual di luar xamarin stuidio. Lebih suka memprogram di VS lebih dari xamarin studio sebanyak mungkin, saya ingin controller sepenuhnya dipisahkan dari kerangka telepon. Dengan cara ini menerapkan ini untuk kerangka kerja lain seperti Android dan Windows Phone akan jauh lebih mudah untuk penggunaan di masa depan.
Saya ingin solusi di mana GUI dapat menanggapi peristiwa tanpa beban berurusan dengan kode switching threading di balik setiap klik tombol. Pada dasarnya biarkan pengontrol kelas menanganinya untuk menjaga kode klien tetap sederhana. Anda mungkin dapat memiliki banyak acara di GUI di mana seolah-olah Anda bisa menanganinya di satu tempat di kelas akan lebih bersih. Saya bukan ahli multi theading, beri tahu saya jika ini salah.
Formulir GUI tidak menyadari controller menjalankan tugas asinkron.
sumber
Ini adalah cara alternatif jika objek yang Anda kerjakan tidak memilikinya
Ini berguna jika Anda bekerja dengan form utama di kelas selain form utama dengan objek yang ada di form utama, tetapi tidak memiliki InvokeRequired
Ini berfungsi sama seperti di atas, tetapi ini adalah pendekatan yang berbeda jika Anda tidak memiliki objek dengan invokerequired, tetapi memiliki akses ke MainForm
sumber
Sepanjang baris yang sama dengan jawaban sebelumnya, tetapi tambahan yang sangat singkat yang Memungkinkan untuk menggunakan semua properti Kontrol tanpa memiliki pengecualian invokasi benang silang.
Metode Penolong
Contoh Penggunaan
sumber
sumber
Misalnya untuk mendapatkan teks dari Kontrol utas UI:
sumber
Pertanyaan yang sama: bagaimana cara memperbarui gui-dari-thread-in-c lainnya
Dua arah:
Kembalikan nilai dalam e.result dan gunakan untuk menetapkan nilai kotak teks Anda di backgroundWorker_RunWorkerCompleted event
Deklarasikan beberapa variabel untuk menyimpan nilai-nilai semacam ini di kelas yang terpisah (yang akan berfungsi sebagai pemegang data). Buat instance statis dari kelas ini dan Anda dapat mengaksesnya melalui utas apa pun.
Contoh:
sumber
Cukup gunakan ini:
sumber
Tindakan y; // dideklarasikan di dalam kelas
label1.Invoke (y = () => label1.Text = "text");
sumber
Cara sederhana dan dapat digunakan kembali untuk mengatasi masalah ini.
Metode Perpanjangan
Contoh Penggunaan
sumber
Ada dua opsi untuk operasi lintas thread.
dan yang kedua adalah menggunakan
Control.InvokeRequired hanya berguna ketika kontrol yang bekerja diwarisi dari kelas Control sementara SynchronizationContext dapat digunakan di mana saja. Beberapa informasi bermanfaat adalah sebagai tautan berikut
UI Pembaruan Lintas Thread | .Bersih
UI Pembaruan Cross Thread menggunakan SynchronizationContext | .Bersih
sumber