Apakah peristiwa C # sinkron?

104

Ada dua bagian dari pertanyaan ini:

  1. Apakah memunculkan acara memblokir utas, atau apakah itu memulai eksekusi EventHandlers secara asinkron dan utas terus berlanjut pada saat yang sama?

  2. Apakah EventHandler individu (berlangganan ke acara) berjalan serentak satu demi satu, atau apakah mereka berjalan secara asinkron tanpa jaminan bahwa orang lain tidak berjalan pada waktu yang sama?

Alexander Bird
sumber

Jawaban:

37

Untuk menjawab pertanyaan Anda:

  1. Memunculkan acara memang memblokir utas jika semua penangan acara diterapkan secara sinkron.
  2. Penangan acara dijalankan secara berurutan, satu demi satu, dalam urutan mereka berlangganan acara tersebut.

Saya juga ingin tahu tentang mekanisme internal eventdan operasinya yang terkait. Jadi saya menulis program sederhana dan terbiasa ildasmmelihat-lihat implementasinya.

Jawaban singkatnya adalah

  • tidak ada operasi asinkron yang terlibat dalam berlangganan atau menjalankan acara.
  • acara diimplementasikan dengan bidang delegasi dukungan dari jenis delegasi yang sama
  • berlangganan selesai dengan Delegate.Combine()
  • berhenti berlangganan selesai dengan Delegate.Remove()
  • Pemanggilan dilakukan dengan hanya memanggil delegasi gabungan terakhir

Inilah yang saya lakukan. Program yang saya gunakan:

public class Foo
{
    // cool, it can return a value! which value it returns if there're multiple 
    // subscribers? answer (by trying): the last subscriber.
    public event Func<int, string> OnCall;
    private int val = 1;

    public void Do()
    {
        if (OnCall != null) 
        {
            var res = OnCall(val++);
            Console.WriteLine($"publisher got back a {res}");
        }
    }
}

public class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo();

        foo.OnCall += i =>
        {
            Console.WriteLine($"sub2: I've got a {i}");
            return "sub2";
        };

        foo.OnCall += i =>
        {
            Console.WriteLine($"sub1: I've got a {i}");
            return "sub1";
        };

        foo.Do();
        foo.Do();
    }
}

Berikut implementasi Foo:

masukkan deskripsi gambar di sini

Perhatikan bahwa ada lapangan OnCall dan acara OnCall . Lapangan OnCalljelas merupakan properti pendukung. Dan itu hanya Func<int, string>, tidak ada yang mewah di sini.

Sekarang bagian yang menarik adalah:

  • add_OnCall(Func<int, string>)
  • remove_OnCall(Func<int, string>)
  • dan bagaimana OnCalldipanggilDo()

Bagaimana Berlangganan dan Berhenti Berlangganan Diterapkan?

Berikut add_OnCallimplementasi yang disingkat di CIL. Bagian yang menarik adalah digunakan Delegate.Combineuntuk menggabungkan dua delegasi.

.method public hidebysig specialname instance void 
        add_OnCall(class [mscorlib]System.Func`2<int32,string> 'value') cil managed
{
  // ...
  .locals init (class [mscorlib]System.Func`2<int32,string> V_0,
           class [mscorlib]System.Func`2<int32,string> V_1,
           class [mscorlib]System.Func`2<int32,string> V_2)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      class [mscorlib]System.Func`2<int32,string> ConsoleApp1.Foo::OnCall
  // ...
  IL_000b:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                          class [mscorlib]System.Delegate)
  // ...
} // end of method Foo::add_OnCall

Demikian juga, Delegate.Removedigunakan dalam remove_OnCall.

Bagaimana sebuah acara dipanggil?

Untuk memanggil OnCalldi Do(), itu hanya menyebut delegasi bersambung final setelah memuat arg yang:

IL_0026:  callvirt   instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)

Bagaimana tepatnya pelanggan berlangganan suatu acara?

Dan akhirnya, Maintidak mengherankan, berlangganan ke OnCallacara dilakukan dengan memanggil add_OnCallmetode pada Fooinstance.

KFL
sumber
3
Sudah selesai dilakukan dengan baik!! Sudah lama sekali sejak saya menanyakan pertanyaan ini. Jika Anda dapat meletakkan verbiage di atas yang langsung menjawab pertanyaan dua bagian saya (yaitu, "jawaban # 1 adalah tidak; jawaban # 2 adalah tidak") maka saya akan menjadikan ini jawaban resmi. Saya mempertaruhkan posting Anda sebagai semua bagian untuk menjawab pertanyaan asli saya, tetapi karena saya tidak menggunakan C # lagi (dan googler lain mungkin baru mengenal konsep ini), itulah mengapa saya meminta verbiage yang membuat jawabannya jelas.
Alexander Bird
Terima kasih @AlexanderBird, baru saja mengeditnya untuk menempatkan jawaban di urutan teratas.
KFL
@KFL, masih belum jelas, saya baru saja akan meninggalkan komentar yang sama dengan Alex. Sederhana "Ya, mereka sinkron", akan membantu
johnny 5
71

Ini adalah jawaban umum dan mencerminkan perilaku default:

  1. Ya, itu memblokir utas, jika metode yang berlangganan acara tidak asinkron.
  2. Mereka dieksekusi satu demi satu. Ini memiliki perubahan lain: Jika satu penangan peristiwa melontarkan pengecualian, penangan peristiwa yang belum dieksekusi tidak akan dijalankan.

Karena itu, setiap kelas yang menyediakan event dapat memilih untuk mengimplementasikan eventnya secara asynchronous. IDesign menyediakan kelas yang disebut EventsHelperyang menyederhanakan ini.

[Note] tautan ini mengharuskan Anda memberikan alamat email untuk mengunduh kelas EventsHelper. (Saya tidak berafiliasi dengan cara apa pun)

Daniel Hilgarth
sumber
Saya telah membaca beberapa posting forum, yang dua di antaranya bertentangan dengan poin pertama, namun tidak memberikan alasan yang tepat. Saya tidak meragukan jawaban Anda, (sesuai dengan yang saya alami selama ini) apakah ada dokumentasi resmi untuk poin pertama? Saya perlu yakin tentang ini, tetapi saya mengalami kesulitan dalam menemukan sesuatu yang resmi tentang masalah ini.
Adam LS
@ AdamL.S. Ini adalah masalah bagaimana acara itu disebut. Jadi itu sangat tergantung pada kelas yang menyediakan acara tersebut.
Daniel Hilgarth
14

Delegasi yang berlangganan acara dipanggil secara sinkron sesuai urutan penambahannya. Jika salah satu delegasi melontarkan pengecualian, yang berikut ini tidak akan dipanggil.

Karena peristiwa ditentukan dengan delegasi multicast, Anda dapat menulis mekanisme pengaktifan Anda sendiri menggunakan

Delegate.GetInvocationList();

dan memanggil delegasi secara asynchronous;

Vladimir
sumber
12

Acara hanyalah susunan delegasi. Selama panggilan delegasi sinkron, acara juga akan sinkron.

Andrey Agibalov
sumber
3

Acara di C # berjalan serentak (dalam kedua kasus), selama Anda tidak memulai utas kedua secara manual.

Doc Brown
sumber
Bagaimana dengan saya menggunakan event handler async? Akankah itu berjalan di utas lain? Saya telah mendengar tentang "Asinkron sepanjang waktu", tetapi sepertinya penangan acara asinkron memiliki utasnya sendiri? Saya tidak mengerti: / Bisakah Anda mencerahkan saya?
Pemain sayap Sendon
3

Acara bersifat sinkron. Inilah sebabnya mengapa siklus hidup acara bekerja seperti itu. Init terjadi sebelum pemuatan, pemuatan terjadi sebelum render, dll.

Jika tidak ada penangan yang ditentukan untuk sebuah acara, siklus akan langsung menyala. Jika lebih dari satu penangan ditentukan, mereka akan dipanggil secara berurutan dan salah satu tidak dapat melanjutkan sampai penangan lainnya selesai sepenuhnya.

Bahkan panggilan asinkron pun sinkron sampai tingkat tertentu. Tidak mungkin untuk memanggil akhir sebelum permulaan selesai.

Joel Etherton
sumber