Panggil (Delegasi)

93

Adakah yang bisa menjelaskan pernyataan yang tertulis di tautan ini

Invoke(Delegate):

Menjalankan delegasi yang ditentukan pada utas yang memiliki pegangan jendela yang mendasari kontrol.

Adakah yang bisa menjelaskan apa artinya (terutama yang berani) saya tidak bisa mendapatkannya dengan jelas

pengguna1903439
sumber
4
Jawaban atas pertanyaan ini terkait dengan properti Control.InvokeRequired - lihat msdn.microsoft.com/en-us/library/…
tanda hubung

Jawaban:

131

Jawaban atas pertanyaan ini terletak pada cara kerja Kontrol C #

Kontrol dalam Formulir Windows terikat ke utas tertentu dan tidak aman utas. Oleh karena itu, jika Anda memanggil metode kontrol dari utas yang berbeda, Anda harus menggunakan salah satu metode pemanggilan kontrol untuk mengatur panggilan ke utas yang tepat. Properti ini bisa digunakan untuk menentukan apakah Anda harus memanggil metode pemanggilan, yang bisa berguna jika Anda tidak tahu thread apa yang memiliki kontrol.

Dari Control.InvokeRequired

Secara efektif, apa yang dilakukan Invoke adalah memastikan bahwa kode yang Anda panggil terjadi pada thread yang kontrolnya "aktif" secara efektif mencegah pengecualian lintas thread.

Dari perspektif historis, di .Net 1.1, ini sebenarnya diizinkan. Maksudnya adalah Anda dapat mencoba dan mengeksekusi kode pada utas "GUI" dari utas latar belakang mana pun dan ini sebagian besar akan berfungsi. Terkadang itu hanya akan menyebabkan aplikasi Anda keluar karena Anda secara efektif mengganggu utas GUI ketika sedang melakukan sesuatu yang lain. Ini adalah Pengecualian Berulir Lintas - bayangkan mencoba memperbarui TextBox saat GUI melukis sesuatu yang lain.

  • Tindakan mana yang diprioritaskan?
  • Apakah mungkin keduanya terjadi sekaligus?
  • Apa yang terjadi pada semua perintah lain yang perlu dijalankan GUI?

Secara efektif, Anda mengganggu antrean, yang dapat menimbulkan banyak konsekuensi tak terduga. Memanggil secara efektif adalah cara "sopan" untuk memasukkan apa yang ingin Anda lakukan ke dalam antrean tersebut, dan aturan ini diberlakukan dari .Net 2.0 dan seterusnya melalui InvalidOperationException yang dilempar .

Untuk memahami apa yang sebenarnya terjadi di balik layar, dan apa yang dimaksud dengan "GUI Thread", ada gunanya untuk memahami apa itu Message Pump atau Message Loop.

Ini sebenarnya sudah dijawab dalam pertanyaan " Apa itu Pompa Pesan " dan bacaan yang direkomendasikan untuk memahami mekanisme aktual yang Anda gunakan saat berinteraksi dengan kontrol.

Bacaan lain yang mungkin berguna bagi Anda termasuk:

Ada apa dengan Begin Invoke

Salah satu aturan utama pemrograman Windows GUI adalah bahwa hanya utas yang membuat kontrol yang dapat mengakses dan / atau mengubah isinya (kecuali untuk beberapa pengecualian yang terdokumentasi). Coba lakukan dari utas lain mana pun dan Anda akan mendapatkan perilaku tak terduga mulai dari kebuntuan, hingga pengecualian hingga UI yang setengah diperbarui. Cara yang benar kemudian untuk memperbarui kontrol dari utas lain adalah dengan memposting pesan yang sesuai ke antrian pesan aplikasi. Ketika message pump menjalankan pesan itu, kontrol akan diperbarui, pada thread yang sama yang membuatnya (ingat, pompa pesan berjalan pada thread utama).

dan, untuk ikhtisar kode yang lebih banyak dengan sampel yang representatif:

Operasi Lintas Benang Tidak Valid

// the canonical form (C# consumer)

public delegate void ControlStringConsumer(Control control, string text);  // defines a delegate type

public void SetText(Control control, string text) {
    if (control.InvokeRequired) {
        control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text});  // invoking itself
    } else {
        control.Text=text;      // the "functional part", executing only on the main thread
    }
}

Setelah Anda menghargai InvokeRequired, Anda mungkin ingin mempertimbangkan menggunakan metode ekstensi untuk menggabungkan panggilan ini. Ini dengan cakap tercakup dalam pertanyaan Stack Overflow Membersihkan Kode yang Dikotori dengan Permintaan Diperlukan .

Ada juga tulisan lebih lanjut tentang apa yang terjadi secara historis yang mungkin menarik.

berlari
sumber
68

Sebuah objek kontrol atau jendela di Windows Forms hanyalah pembungkus di sekitar jendela Win32 yang diidentifikasi oleh pegangan (kadang-kadang disebut HWND). Kebanyakan hal yang Anda lakukan dengan kontrol pada akhirnya akan menghasilkan panggilan API Win32 yang menggunakan pegangan ini. Pegangan dimiliki oleh utas yang membuatnya (biasanya utas utama), dan tidak boleh dimanipulasi oleh utas lain. Jika karena alasan tertentu Anda perlu melakukan sesuatu dengan kontrol dari utas lain, Anda dapat menggunakan Invokeutas utama untuk melakukannya atas nama Anda.

Misalnya, jika Anda ingin mengubah teks label dari utas pekerja, Anda dapat melakukan sesuatu seperti ini:

theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
Thomas Levesque
sumber
Bisakah Anda menjelaskan mengapa seseorang melakukan hal seperti ini? this.Invoke(() => this.Enabled = true);Apa pun yang thisdimaksud pasti ada di utas saat ini, bukan?
Kyle Delaney
1
@KDelanan, sebuah objek tidak berada "dalam" sebuah utas, dan utas saat ini belum tentu merupakan utas yang membuat objek tersebut.
Thomas Levesque
24

Jika Anda ingin mengubah kontrol, itu harus dilakukan di thread tempat kontrol dibuat. InvokeMetode ini memungkinkan Anda untuk mengeksekusi metode di thread terkait (thread yang memiliki pegangan jendela yang mendasari kontrol).

Di bawah contoh thread1 melontarkan pengecualian karena SetText1 mencoba mengubah textBox1.Text dari utas lain. Namun di thread2, Tindakan di SetText2 dijalankan di thread tempat TextBox dibuat

private void btn_Click(object sender, EvenetArgs e)
{
    var thread1 = new Thread(SetText1);
    var thread2 = new Thread(SetText2);
    thread1.Start();
    thread2.Start();
}

private void SetText1() 
{
    textBox1.Text = "Test";
}

private void SetText2() 
{
    textBox1.Invoke(new Action(() => textBox1.Text = "Test"));
}
Mehmet Ataş
sumber
Saya sangat suka pendekatan ini, ini menyembunyikan alam yang didelegasikan, tapi itu jalan pintas yang bagus.
shytikov
7
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });
Pengembang Terbaik
sumber
Penggunaan System.Action orang menyarankan tanggapan lain hanya bekerja pada framework 3.5+, untuk versi yang lebih lama, ini bekerja dengan sempurna
Suicide Platypus
2

Dalam istilah praktis, ini berarti delegasi dijamin akan dipanggil di thread utama. Ini penting karena dalam kasus kontrol windows jika Anda tidak memperbarui propertinya pada utas utama, maka Anda tidak akan melihat perubahannya, atau kontrol tersebut memunculkan pengecualian.

Polanya adalah:

void OnEvent(object sender, EventArgs e)
{
   if (this.InvokeRequired)
   {
       this.Invoke(() => this.OnEvent(sender, e);
       return;
   }

   // do stuff (now you know you are on the main thread)
}
satnhak
sumber
2

this.Invoke(delegate)pastikan bahwa Anda memanggil delegasi argumen ke this.Invoke()pada utas utama / utas yang dibuat.

Saya dapat mengatakan aturan Thumb tidak mengakses kontrol formulir Anda kecuali dari utas utama.

Mungkin baris berikut masuk akal untuk menggunakan Invoke ()

    private void SetText(string text)
    {
        // InvokeRequired required compares the thread ID of the
        // calling thread to the thread ID of the creating thread.
        // If these threads are different, it returns true.
        if (this.textBox1.InvokeRequired)
        {   
            SetTextCallback d = new SetTextCallback(SetText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.textBox1.Text = text;
        }
    }

Ada beberapa situasi meskipun Anda membuat utas Threadpool (yaitu utas pekerja) itu akan berjalan di utas utama. Ini tidak akan membuat utas baru karena utas utama tersedia untuk memproses instruksi lebih lanjut. Jadi Pertama selidiki apakah utas yang sedang berjalan adalah utas utama menggunakan this.InvokeRequiredif return true, kode saat ini berjalan pada utas pekerja jadi panggil this.Invoke (d, new object [] {text});

else secara langsung memperbarui kontrol UI (Di sini Anda dijamin bahwa Anda menjalankan kode di utas utama.)

Srikanth
sumber
1

Artinya, delegasi akan berjalan di UI thread, meskipun Anda memanggil metode tersebut dari pekerja latar belakang atau thread pool thread. Elemen UI memiliki afinitas utas - mereka hanya suka berbicara langsung ke satu utas: utas UI. Utas UI didefinisikan sebagai utas yang membuat contoh kontrol, dan oleh karena itu dikaitkan dengan pegangan jendela. Tapi semua itu adalah detail implementasi.

Poin utamanya adalah: Anda akan memanggil metode ini dari thread pekerja sehingga Anda dapat mengakses UI (untuk mengubah nilai di label, dll) - karena Anda tidak diizinkan melakukannya dari thread lain selain thread UI.

Marc Gravell
sumber
0

Delegasi pada dasarnya adalah inline Actionatau Func<T>. Anda bisa mendeklarasikan delegasi di luar lingkup metode yang Anda jalankan atau menggunakan lambdaekspresi ( =>); karena Anda menjalankan delegasi dalam sebuah metode, Anda menjalankannya pada utas yang sedang dijalankan untuk jendela / aplikasi saat ini yang sedikit dicetak tebal.

Contoh Lambda

int AddFiveToNumber(int number)
{
  var d = (int i => i + 5);
  d.Invoke(number);
}
LukeHennerley
sumber
0

Artinya, delegasi yang Anda teruskan dieksekusi di thread yang membuat objek Control (yaitu UI thread).

Anda perlu memanggil metode ini ketika aplikasi Anda multi-threaded dan Anda ingin melakukan beberapa operasi UI dari thread selain UI thread, karena jika Anda hanya mencoba memanggil metode pada Kontrol dari thread berbeda, Anda akan mendapatkan System.InvalidOperationException.

pengguna1610015
sumber