เหตุการณ์ C # ซิงโครไนซ์หรือไม่


104

คำถามนี้มีสองส่วน:

  1. การเพิ่มเหตุการณ์บล็อกเธรดหรือไม่หรือเริ่มดำเนินการ EventHandlers แบบอะซิงโครนัสและเธรดจะดำเนินต่อไปในเวลาเดียวกันหรือไม่

  2. เป็นEventHandlers บุคคล (สมัครรับข้อมูลเหตุการณ์) ทำงานพร้อมกันหลังจากนั้นอีกหนึ่งหรือว่าพวกเขาทำงานแบบไม่พร้อมกับการรับประกันว่าคนอื่น ๆ ไม่ได้ทำงานในเวลาเดียวกันหรือไม่?

คำตอบ:


37

เพื่อตอบคำถามของคุณ:

  1. การเพิ่มเหตุการณ์จะบล็อกเธรดหากตัวจัดการเหตุการณ์ทั้งหมดถูกนำไปใช้พร้อมกัน
  2. ตัวจัดการเหตุการณ์จะถูกดำเนินการตามลำดับทีละรายการตามลำดับที่สมัครเป็นสมาชิกเหตุการณ์

ฉันก็อยากรู้เกี่ยวกับกลไกภายในeventและการทำงานที่เกี่ยวข้องเช่นกัน ดังนั้นฉันจึงเขียนโปรแกรมง่ายๆและใช้ildasmเพื่อกระตุ้นการใช้งาน

คำตอบสั้น ๆ คือ

  • ไม่มีการดำเนินการแบบอะซิงโครนัสที่เกี่ยวข้องกับการสมัครหรือเรียกใช้เหตุการณ์
  • เหตุการณ์ถูกนำไปใช้โดยมีเขตข้อมูลผู้รับมอบสิทธิ์สำรองประเภทผู้รับมอบสิทธิ์เดียวกัน
  • สมัครสมาชิกเสร็จสิ้นด้วย Delegate.Combine()
  • ทำการยกเลิกการสมัครด้วยไฟล์ Delegate.Remove()
  • การเรียกใช้ทำได้โดยเพียงแค่เรียกผู้ร่วมประชุมที่รวมกันสุดท้าย

นี่คือสิ่งที่ฉันทำ โปรแกรมที่ฉันใช้:

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

นี่คือการใช้งานของ Foo:

ใส่คำอธิบายภาพที่นี่

โปรดทราบว่ามีข้อมูล OnCallและเหตุการณ์ เห็นได้ชัดว่าOnCallฟิลด์OnCallเป็นคุณสมบัติสำรอง และมันเป็นเพียงแค่Func<int, string>ไม่มีอะไรน่าสนใจที่นี่

ตอนนี้ส่วนที่น่าสนใจ ได้แก่

  • add_OnCall(Func<int, string>)
  • remove_OnCall(Func<int, string>)
  • และวิธีOnCallเรียกใช้ในDo()

การสมัครสมาชิกและการยกเลิกการสมัครใช้งานอย่างไร?

นี่คือการadd_OnCallใช้งานแบบย่อใน CIL ส่วนที่น่าสนใจคือใช้Delegate.Combineในการเชื่อมต่อผู้รับมอบสิทธิ์สองคน

.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

ในทำนองเดียวกันDelegate.Removeใช้ในremove_OnCall.

มีการเรียกเหตุการณ์อย่างไร?

จะก่อให้เกิดOnCallในDo()มันก็เรียกผู้แทนการตัดแบ่งสุดท้ายหลังจากที่โหลดหาเรื่อง:

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

สมาชิกสมัครเข้าร่วมกิจกรรมได้อย่างไร?

และในที่สุดก็Mainไม่น่าแปลกใจที่การสมัครเข้าร่วมOnCallกิจกรรมทำได้โดยadd_OnCallวิธีการโทรบนFooอินสแตนซ์


3
ทำได้ดี!! นานมากแล้วที่ฉันถามคำถามนี้ หากคุณสามารถใส่คำฟุ่มเฟือยไว้ด้านบนซึ่งจะตอบคำถามสองส่วนของฉันได้โดยตรง (เช่น "คำตอบ # 1 คือไม่; # 2 คำตอบคือไม่") ฉันจะทำให้คำตอบนี้เป็นคำตอบอย่างเป็นทางการ ฉันพนันว่าโพสต์ของคุณเป็นชิ้นส่วนทั้งหมดเพื่อตอบคำถามเดิมของฉัน แต่เนื่องจากฉันไม่ได้ใช้ C # อีกต่อไป (และ Googler คนอื่น ๆ อาจยังใหม่กับแนวคิดเหล่านี้) นั่นคือเหตุผลที่ฉันขอคำฟุ่มเฟือยซึ่งทำให้คำตอบชัดเจน
Alexander Bird

ขอบคุณ @AlexanderBird เพิ่งแก้ไขเพื่อให้คำตอบอยู่ด้านบน
KFL

@KFL ยังไม่ชัดเจนฉันแค่จะแสดงความคิดเห็นเดียวกับอเล็กซ์ คำง่ายๆ "ใช่มันซิงโครนัส" จะเป็นประโยชน์
5

71

นี่คือคำตอบทั่วไปและสะท้อนถึงพฤติกรรมเริ่มต้น:

  1. ใช่มันบล็อกเธรดหากวิธีการสมัครเข้าร่วมเหตุการณ์ไม่ตรงกัน
  2. พวกเขาจะถูกประหารชีวิตทีละคน สิ่งนี้มีการเปลี่ยนแปลงอีกครั้ง: หากตัวจัดการเหตุการณ์หนึ่งเกิดข้อยกเว้นตัวจัดการเหตุการณ์ที่ยังไม่ได้ดำเนินการจะไม่ถูกดำเนินการ

ต้องบอกว่าทุกชั้นเรียนที่มีกิจกรรมสามารถเลือกที่จะใช้เหตุการณ์แบบอะซิงโครนัสได้ IDesignจัดเตรียมคลาสที่เรียกEventsHelperว่าทำให้สิ่งนี้ง่ายขึ้น

[หมายเหตุ]ลิงก์นี้กำหนดให้คุณต้องระบุที่อยู่อีเมลเพื่อดาวน์โหลดคลาส EventsHelper (ฉันไม่มีส่วนเกี่ยวข้อง แต่อย่างใด)


ฉันได้อ่านโพสต์ในฟอรัมสองสามโพสต์ซึ่งทั้งสองได้ขัดแย้งกับประเด็นแรกโดยไม่ได้ให้เหตุผลที่เหมาะสม ฉันไม่สงสัยในคำตอบของคุณ (ตรงกับสิ่งที่ฉันประสบมาจนถึงตอนนี้) มีเอกสารอย่างเป็นทางการในประเด็นแรกหรือไม่? ฉันต้องแน่ใจเกี่ยวกับเรื่องนี้ แต่ฉันมีปัญหาในการค้นหาอะไรที่เป็นทางการเกี่ยวกับเรื่องนี้
Adam LS

@ อดัมแอล. เอส. มันเป็นเรื่องของวิธีการที่เรียกว่าเหตุการณ์ ดังนั้นจึงขึ้นอยู่กับคลาสที่จัดกิจกรรม
Daniel Hilgarth

14

ผู้ได้รับมอบหมายที่สมัครเข้าร่วมกิจกรรมจะถูกเรียกพร้อมกันตามลำดับที่เพิ่ม หากผู้ได้รับมอบหมายคนใดคนหนึ่งโยนข้อยกเว้นบุคคลที่ตามมาจะไม่ถูกเรียก

เนื่องจากเหตุการณ์ถูกกำหนดด้วยผู้รับมอบสิทธิ์แบบหลายผู้รับคุณสามารถเขียนกลไกการยิงของคุณเองโดยใช้

Delegate.GetInvocationList();

และเรียกผู้รับมอบสิทธิ์แบบอะซิงโครนัส


12

เหตุการณ์เป็นเพียงอาร์เรย์ของผู้ได้รับมอบหมาย ตราบใดที่การโทรของผู้ร่วมประชุมเป็นแบบซิงโครนัสเหตุการณ์จะซิงโครนัสด้วย


7

โดยทั่วไปเหตุการณ์เป็นแบบซิงโครนัส อย่างไรก็ตามมีข้อยกเว้นบางประการเช่นSystem.Timers.Timer.Elapsedเหตุการณ์ที่เกิดขึ้นบนThreadPoolเธรดหากSyncronisingObjectเป็นโมฆะ

เอกสาร: http://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed.aspx


3

เหตุการณ์ใน C # ทำงานพร้อมกัน (ในทั้งสองกรณี) ตราบใดที่คุณไม่เริ่มเธรดที่สองด้วยตนเอง


ฉันจะใช้ตัวจัดการเหตุการณ์ async ได้อย่างไร มันจะทำงานในเธรดอื่นหรือไม่ ฉันเคยได้ยินเกี่ยวกับ "Async all the way" แต่ดูเหมือนว่า async event handlers จะมีเธรดของตัวเองหรือไม่ ฉันไม่เข้าใจ: / คุณช่วยสอนฉันได้ไหม?
Winger Sendon

3

เหตุการณ์เป็นแบบซิงโครนัส นี่คือสาเหตุที่วงจรชีวิตของเหตุการณ์ทำงานในลักษณะนี้ Inits เกิดขึ้นก่อนโหลดโหลดเกิดขึ้นก่อนแสดงผล ฯลฯ

หากไม่มีการระบุตัวจัดการสำหรับเหตุการณ์วงจรจะสว่างไสว หากระบุตัวจัดการมากกว่าหนึ่งตัวพวกเขาจะถูกเรียกตามลำดับและหนึ่งไม่สามารถดำเนินการต่อได้จนกว่าอีกตัวจัดการจะเสร็จสมบูรณ์

แม้แต่การโทรแบบอะซิงโครนัสก็ซิงโครนัสในระดับหนึ่ง คงเป็นไปไม่ได้ที่จะเรียกจุดจบก่อนที่การเริ่มต้นจะเสร็จสมบูรณ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.