Çağır (Delege)


93

Lütfen bu bağlantıya yazılan bu ifadeyi açıklayabilir misiniz?

Invoke(Delegate):

Denetimin temelindeki pencere tutamacına sahip olan iş parçacığı üzerinde belirtilen temsilciyi yürütür .

Bunun ne anlama geldiğini kimse açıklayabilir mi (özellikle cesur olanı) Açıkça anlayamıyorum


4
Bu sorunun cevabı Control.InvokeRequired özelliğine bağlıdır - bkz msdn.microsoft.com/en-us/library/...
çizgi

Yanıtlar:


131

Bu sorunun cevabı, C # Kontrollerinin nasıl çalıştığına bağlıdır

Windows Forms'daki denetimler belirli bir iş parçacığına bağlıdır ve iş parçacığı güvenli değildir. Bu nedenle, farklı bir iş parçacığından bir denetimin yöntemini çağırıyorsanız, çağrıyı uygun iş parçacığına sıralamak için denetimin çağırma yöntemlerinden birini kullanmanız gerekir. Bu özellik, hangi iş parçacığının bir denetime sahip olduğunu bilmiyorsanız faydalı olabilecek bir invoke yöntemini çağırmanız gerekip gerekmediğini belirlemek için kullanılabilir.

Gönderen Control.InvokeRequired

Etkili olarak, Invoke'un yaptığı şey, aradığınız kodun, denetimin "yaşadığı" iş parçacığı üzerinde oluşmasını ve çapraz iş parçacıklı istisnaları etkili bir şekilde engellemesini sağlamaktır.

Tarihsel bir bakış açısıyla, .Net 1.1'de buna gerçekten izin veriliyordu. Bunun anlamı, herhangi bir arka plan iş parçacığından "GUI" iş parçacığı üzerinde kod çalıştırmayı deneyebileceğiniz ve bu çoğunlukla işe yarayacağıdır. Bazen, başka bir şey yaparken GUI iş parçacığını etkin bir şekilde kesintiye uğrattığınız için uygulamanızın çıkmasına neden olur. Bu Çapraz İş Parçacıklı İstisnadır - GUI başka bir şeyi boyarken bir TextBox'ı güncellemeye çalıştığınızı hayal edin.

  • Hangi eylem önceliklidir?
  • Her ikisinin de aynı anda olması mümkün mü?
  • GUI'nin çalıştırması gereken diğer tüm komutlara ne olur?

Etkili bir şekilde, öngörülemeyen birçok sonucu olabilecek bir kuyruğu kesiyorsunuz. Invoke, yapmak istediğiniz şeyi o kuyruğa almanın etkili bir "kibar" yoludur ve bu kural .Net 2.0'dan itibaren atılan bir InvalidOperationException ile uygulanmıştır .

Perde arkasında gerçekte neler olup bittiğini ve "GUI İş Parçacığı" ile ne kastedildiğini anlamak için, bir Mesaj Pompasının veya Mesaj Döngüsünün ne olduğunu anlamak faydalıdır.

Bu aslında " Mesaj Pompası Nedir " sorusunda cevaplanmıştır ve kontrollerle etkileşim kurarken bağladığınız gerçek mekanizmayı anlamak için okumanız önerilir.

Yararlı bulabileceğiniz diğer okumalar şunları içerir:

Begin Invoke ile ne var?

Windows GUI programlamasının temel kurallarından biri, yalnızca bir denetimi oluşturan iş parçacığının içeriğine erişebilmesi ve / veya içeriklerini değiştirebilmesidir (birkaç belgelenmiş istisna dışında). Bunu başka bir iş parçacığından yapmayı deneyin ve kilitlenmeden istisnalara ve yarı güncellenmiş bir kullanıcı arayüzüne kadar öngörülemeyen davranışlar elde edeceksiniz. Başka bir iş parçacığından bir denetimi güncellemenin doğru yolu, uygulama ileti kuyruğuna uygun bir ileti göndermektir. Pompa mesajı bu mesajı yürütmek üzereyken, kontrol kendisini oluşturan iş parçacığı üzerinde güncellenecektir (unutmayın, ileti pompası ana iş parçacığı üzerinde çalışır).

ve temsili bir örnekle daha fazla kod ağırlıklı bir genel bakış için:

Geçersiz Çapraz İş Parçacığı İşlemleri

// 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
    }
}

InvokeRequired'ı takdir ettiğinizde, bu çağrıları tamamlamak için bir uzatma yöntemi kullanmayı düşünebilirsiniz. Bu, Yığın Taşması sorusunda ustalıkla kapsanmıştır .

Tarihsel olarak ne olduğuna dair ilginç olabilecek başka bir yazı da var .


68

Windows Forms'daki bir kontrol veya pencere nesnesi, bir tutamaçla (bazen HWND olarak adlandırılır) tanımlanan bir Win32 penceresinin etrafındaki bir sarmalayıcıdır . Denetimle yaptığınız çoğu şey, sonunda bu tanıtıcıyı kullanan bir Win32 API çağrısıyla sonuçlanacaktır. Tanıtıcı, kendisini oluşturan evreye (tipik olarak ana iş parçacığı) aittir ve başka bir iş parçacığı tarafından değiştirilmemelidir. Herhangi bir nedenle başka bir iş parçacığındaki denetimle bir şeyler yapmanız gerekiyorsa Invoke, ana iş parçacığından bunu sizin adınıza yapmasını isteyebilirsiniz.

Örneğin, bir etiketin metnini bir işçi dizisinden değiştirmek istiyorsanız, bunun gibi bir şey yapabilirsiniz:

theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));

Birinin neden böyle bir şey yaptığını açıklayabilir misin? this.Invoke(() => this.Enabled = true);Ne thisifade ederse, kesinlikle mevcut ileti dizisinde var, değil mi?
Kyle Delaney

1
@KyleDelaney, bir nesne bir iş parçacığı içinde değildir ve geçerli iş parçacığı, nesneyi oluşturan iş parçacığı olmak zorunda değildir.
Thomas Levesque

24

Bir kontrolü değiştirmek istiyorsanız, kontrolün yaratıldığı iş parçacığında yapılmalıdır. Bu Invokeyöntem, ilişkili iş parçacığındaki (denetimin temelindeki pencere tutamacına sahip olan iş parçacığı) yöntemleri yürütmenize olanak tanır.

Aşağıdaki örnekte thread1 bir istisna atar çünkü SetText1 başka bir thread'den textBox1.Text'i değiştirmeye çalışıyor. Ancak thread2'de, SetText2'deki Action TextBox'ın oluşturulduğu thread içinde yürütülür.

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"));
}

Bu yaklaşımı gerçekten seviyorum, delegelerin doğasını gizliyor, ama yine de güzel bir kısayol.
shytikov

7
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });

System.Action kullanımı, diğer yanıtlarda yalnızca 3.5+ çerçevesinde çalıştığını öne sürüyor, eski sürümler için bu mükemmel çalışıyor
Suicide Platypus

2

Pratik anlamda, temsilcinin ana iş parçacığı üzerinde çağrılmasının garantili olduğu anlamına gelir. Bu önemlidir, çünkü Windows kontrolleri söz konusu olduğunda, ana iş parçacığı üzerindeki özelliklerini güncellemezseniz, o zaman değişikliği görmezsiniz veya kontrol bir istisna oluşturur.

Desen şu şekildedir:

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)
}

2

this.Invoke(delegate)this.Invoke()Ana iş parçacığı / oluşturulan iş parçacığı üzerinde delegeyi argümanına çağırdığınızdan emin olun .

Başparmak kuralının ana iş parçacığı dışında form kontrollerinize erişmediğini söyleyebilirim.

Invoke () kullanmak için aşağıdaki satırlar mantıklı olabilir

    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;
        }
    }

Bir Threadpool iş parçacığı (yani işçi iş parçacığı) oluşturmanıza rağmen, ana iş parçacığı üzerinde çalışacağı durumlar vardır. Yeni bir iş parçacığı oluşturmayacak coz ana iş parçacığı, sonraki talimatları işlemek için kullanılabilir. Öyleyse Öncelikle, mevcut çalışan iş parçacığının ana iş parçacığı olup olmadığını araştırın, this.InvokeRequiredeğer doğru döndürürse, geçerli kod çalışan iş parçacığı üzerinde çalışıyor, bu nedenle bunu çağırın. İnvoke (d, yeni nesne [] {metin});

aksi takdirde doğrudan UI kontrolünü güncelleyin (Burada kodu ana iş parçacığı üzerinde çalıştırdığınız garantilidir.)


1

Bu, bu yöntemi bir arka plan çalışanı veya iş parçacığı havuzu iş parçacığından çağırsanız bile, temsilcinin UI iş parçacığında çalışacağı anlamına gelir. UI öğelerinin iş parçacığı benzeşimi vardır - yalnızca doğrudan bir iş parçacığıyla konuşmayı severler: UI iş parçacığı. UI iş parçacığı, kontrol örneğini oluşturan iş parçacığı olarak tanımlanır ve bu nedenle pencere tutamacıyla ilişkilendirilir. Ama bunların hepsi bir uygulama detayı.

Kilit nokta şudur: Bu yöntemi bir çalışan iş parçacığından çağırırsınız, böylece UI'ye erişebilirsiniz (bir etiketteki değeri değiştirmek için vb.) - çünkü bunu UI iş parçacığından başka herhangi bir iş parçacığından yapmanıza izin verilmiyor .


0

Temsilci, esasen satır içi Actionveya Func<T>. Çalıştırdığınız veya bir lambdaifade ( =>) kullandığınız bir yöntemin kapsamı dışında bir temsilci bildirebilirsiniz ; Temsilciyi bir yöntem içinde çalıştırdığınız için, onu yürürlükteki pencere / uygulama için çalıştırılan iş parçacığı üzerinde çalıştırırsınız, bu bit kalın harflidir.

Lambda örneği

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

0

Bu, ilettiğiniz temsilcinin Control nesnesini (UI iş parçacığı olan) oluşturan iş parçacığı üzerinde yürütüldüğü anlamına gelir.

Uygulamanız çok iş parçacıklı olduğunda ve UI iş parçacığından başka bir iş parçacığından bazı UI işlemleri yapmak istediğinizde bu yöntemi çağırmanız gerekir, çünkü yalnızca farklı bir iş parçacığından bir Denetimde bir yöntem çağırmaya çalışırsanız, bir System.InvalidOperationException.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.