Các sự kiện C # có đồng bộ không?


104

Có hai phần cho câu hỏi này:

  1. Việc nâng cao một sự kiện có chặn luồng hay nó bắt đầu thực thi EventHandlers một cách không đồng bộ và luồng tiếp tục diễn ra cùng một lúc?

  2. Các EventHandler riêng lẻ (đã đăng ký sự kiện) chạy đồng bộ lần lượt hay chúng chạy không đồng bộ mà không đảm bảo rằng những người khác không chạy cùng lúc?

Câu trả lời:


37

Để trả lời câu hỏi của bạn:

  1. Việc nâng một sự kiện sẽ chặn luồng nếu tất cả các trình xử lý sự kiện đều được triển khai đồng bộ.
  2. Các trình xử lý sự kiện được thực hiện tuần tự, cái khác, theo thứ tự chúng được đăng ký vào sự kiện.

Tôi cũng tò mò về cơ chế bên trong eventvà các hoạt động liên quan của nó. Vì vậy, tôi đã viết một chương trình đơn giản và sử dụng ildasmđể xem xét việc triển khai nó.

Câu trả lời ngắn gọn là

  • không có hoạt động không đồng bộ nào liên quan đến việc đăng ký hoặc gọi các sự kiện.
  • sự kiện được triển khai với trường đại biểu ủng hộ của cùng một loại đại biểu
  • đăng ký được thực hiện với Delegate.Combine()
  • hủy đăng ký được thực hiện với Delegate.Remove()
  • Việc gọi được thực hiện bằng cách chỉ cần gọi đại biểu kết hợp cuối cùng

Đây là những gì tôi đã làm. Chương trình tôi đã sử dụng:

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

Đây là cách triển khai của Foo:

nhập mô tả hình ảnh ở đây

Lưu ý rằng có một trường OnCall và một sự kiện OnCall . Lĩnh vực OnCallrõ ràng là tài sản hỗ trợ. Và nó chỉ đơn thuần là một Func<int, string>, không có gì lạ mắt ở đây.

Bây giờ các phần thú vị là:

  • add_OnCall(Func<int, string>)
  • remove_OnCall(Func<int, string>)
  • và làm thế nào OnCallđược gọi trongDo()

Đăng ký và Hủy đăng ký được triển khai như thế nào?

Đây là cách add_OnCalltriển khai viết tắt trong CIL. Phần thú vị là nó sử dụng Delegate.Combineđể nối hai đại biểu.

.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

Tương tự như vậy, Delegate.Removeđược sử dụng trong remove_OnCall.

Làm thế nào một sự kiện được gọi ra?

Để gọi OnCallvào Do(), nó chỉ cần gọi đại biểu được nối cuối cùng sau khi tải đối số:

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

Chính xác thì người đăng ký tham gia một sự kiện như thế nào?

Và cuối cùng, Mainkhông ngạc nhiên, việc đăng ký OnCallsự kiện được thực hiện bằng add_OnCallphương thức gọi trên Foophiên bản.


3
Làm tốt!! Đã rất lâu rồi tôi không hỏi câu hỏi này. Nếu bạn có thể đặt chi tiết ở đầu câu trả lời trực tiếp cho câu hỏi hai phần của tôi (tức là "câu trả lời số 1 là không; câu trả lời số 2 là không") thì tôi sẽ coi đây là câu trả lời chính thức. Tôi đặt cược bài đăng của bạn là tất cả các phần để trả lời các câu hỏi ban đầu của tôi, nhưng vì tôi không sử dụng C # nữa (và những người dùng google khác có thể còn mới mẻ với những khái niệm này) nên tôi yêu cầu chi tiết để làm cho câu trả lời rõ ràng.
Alexander Bird

Cảm ơn @AlexanderBird, chỉ cần chỉnh sửa nó để đặt câu trả lời ở đầu.
KFL

@KFL, nó vẫn chưa rõ ràng, tôi chỉ định để lại nhận xét giống như Alex. Một câu đơn giản "Có, chúng đồng bộ", sẽ hữu ích
johnny 5

71

Đây là câu trả lời chung và phản ánh hành vi mặc định:

  1. Có, nó chặn luồng, nếu các phương thức đăng ký sự kiện không đồng bộ.
  2. Chúng được thực hiện lần lượt. Điều này có một bước ngoặt khác: Nếu một trình xử lý sự kiện ném một ngoại lệ, các trình xử lý sự kiện chưa được thực thi sẽ không được thực thi.

Phải nói rằng, mọi lớp cung cấp sự kiện có thể chọn triển khai sự kiện của nó một cách không đồng bộ. IDesign cung cấp một lớp được gọi là EventsHelperđơn giản hóa việc này.

[Lưu ý] liên kết này yêu cầu bạn cung cấp địa chỉ e-mail để tải xuống lớp EventsHelper. (Tôi không liên kết dưới bất kỳ hình thức nào)


Tôi đã đọc một vài bài đăng trên diễn đàn, trong đó hai bài đã mâu thuẫn với điểm đầu tiên, trong khi không đưa ra được lý do chính đáng. Tôi không nghi ngờ câu trả lời của bạn, (nó khớp với những gì tôi đã trải qua cho đến nay) có tài liệu chính thức nào về điểm đầu tiên không? Tôi cần chắc chắn về điều này, nhưng tôi gặp khó khăn trong việc tìm kiếm bất kỳ điều gì chính thức về vấn đề chính xác này.
Adam LS

@ AdamL.S. Vấn đề là sự kiện được gọi như thế nào. Vì vậy, nó thực sự phụ thuộc vào lớp cung cấp sự kiện.
Daniel Hilgarth

14

Các đại biểu đã đăng ký sự kiện được gọi đồng bộ theo thứ tự đã được thêm vào. Nếu một trong các đại biểu ném một ngoại lệ, những đại biểu sau sẽ không được gọi.

Vì các sự kiện được xác định với các đại biểu đa hướng, bạn có thể viết cơ chế kích hoạt của riêng mình bằng cách sử dụng

Delegate.GetInvocationList();

và triệu tập các đại biểu một cách không đồng bộ;


12

Sự kiện chỉ là mảng của các đại biểu. Miễn là cuộc gọi đại biểu là đồng bộ, các sự kiện cũng đồng bộ.



3

Các sự kiện trong C # chạy đồng bộ (trong cả hai trường hợp), miễn là bạn không bắt đầu chuỗi thứ hai theo cách thủ công.


Tôi sử dụng trình xử lý sự kiện không đồng bộ thì sao? Sau đó nó sẽ chạy trong một chủ đề khác? Tôi đã nghe nói về "Không đồng bộ tất cả các cách", nhưng có vẻ như trình xử lý sự kiện không đồng bộ có luồng riêng của họ? Tôi không hiểu: / Bạn vui lòng khai sáng cho tôi được không?
Winger Sendon

3

Các sự kiện là đồng bộ. Đây là lý do tại sao vòng đời sự kiện hoạt động theo cách của nó. Inits xảy ra trước khi tải, tải xảy ra trước khi hiển thị, v.v.

Nếu không có trình xử lý nào được chỉ định cho một sự kiện, thì chu trình sẽ diễn ra nhanh chóng. Nếu nhiều hơn một trình xử lý được chỉ định, chúng sẽ được gọi theo thứ tự và một trình xử lý không thể tiếp tục cho đến khi trình xử lý kia kết thúc hoàn toàn.

Ngay cả các cuộc gọi không đồng bộ cũng đồng bộ ở một mức độ. Sẽ không thể gọi là kết thúc trước khi bắt đầu hoàn thành.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.