Là một vòng lặp sự kiện chỉ là một vòng lặp for / while với việc bỏ phiếu được tối ưu hóa?


55

Tôi đang cố gắng hiểu vòng lặp sự kiện là gì. Thông thường lời giải thích là trong một vòng lặp sự kiện, bạn làm một cái gì đó cho đến khi bạn được thông báo rằng một sự kiện đã xảy ra. Sau đó, bạn xử lý sự kiện và tiếp tục làm những gì bạn đang làm trước đó.

Để ánh xạ định nghĩa trên với một ví dụ. Tôi có một máy chủ 'lắng nghe' trong một vòng lặp sự kiện và khi phát hiện ra kết nối ổ cắm, dữ liệu từ nó sẽ được đọc và hiển thị, sau đó máy chủ sẽ tiếp tục / bắt đầu nghe như trước đây.


Tuy nhiên, sự kiện này xảy ra và chúng tôi nhận được thông báo "giống như vậy" là rất nhiều để tôi xử lý. Bạn có thể nói: "Không phải như thế" bạn phải đăng ký người nghe sự kiện ". Nhưng những gì một người nghe sự kiện nhưng một chức năng mà vì lý do nào đó không trở lại. Có phải trong vòng lặp riêng của nó, chờ đợi để được thông báo khi một sự kiện xảy ra? Người nghe sự kiện cũng nên đăng ký một người nghe sự kiện? Nó kết thúc ở đâu?


Các sự kiện là một sự trừu tượng tốt đẹp để làm việc với, tuy nhiên chỉ là một sự trừu tượng hóa. Tôi tin rằng cuối cùng, bỏ phiếu là không thể tránh khỏi. Có lẽ chúng tôi không làm điều đó trong mã của mình, nhưng các cấp thấp hơn (triển khai ngôn ngữ lập trình hoặc HĐH) đang làm điều đó cho chúng tôi.

Về cơ bản, nó đi xuống mã giả sau đây đang chạy ở một nơi đủ thấp để không phải chờ đợi bận rộn:

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

Đây là sự hiểu biết của tôi về toàn bộ ý tưởng, và tôi muốn nghe nếu điều này là chính xác. Tôi cởi mở khi chấp nhận rằng toàn bộ ý tưởng là sai về cơ bản, trong trường hợp đó tôi muốn giải thích chính xác.


3
Các hệ thống sự kiện là các triển khai của mẫu Observer . Hiểu mô hình nên củng cố sự hiểu biết của bạn về các sự kiện. Không cần bỏ phiếu.
Steven Evers

1
Cuối cùng là có, ngay cả khi ngôn ngữ xây dựng nó trừu tượng đi.
GrandmasterB

@SteveEvers Trong liên kết wiki của bạn. Làm gì EventSourcenếu không bỏ phiếu đầu vào bàn phím?
TheMeaningfulEngineer

@Alan: Nó có thể làm bất cứ điều gì, nhưng đối với đầu vào bàn phím cụ thể, có các API để đăng ký ứng dụng của bạn dưới dạng nghe các sự kiện bàn phím, sau đó bạn có thể sử dụng làm nguồn sự kiện của mình mà không cần bỏ phiếu. Tất nhiên, nếu bạn đi xuống đủ xa, USB luôn luôn bỏ phiếu, nhưng giả sử chúng ta đang sử dụng bàn phím PS / 2, được điều khiển ngắt, và sau đó bạn có ngăn xếp đầu vào bàn phím dựa trên các sự kiện không bỏ phiếu.
Phoshi

Tôi đã có câu hỏi này từ nhiều năm nay, nhưng tôi không thể biết tại sao tôi không bao giờ bận tâm hỏi nó. Cảm ơn, tôi hài lòng với sự hiểu biết @Karl Bielefeldt đã khai sáng cho tôi.
0xc0de

Câu trả lời:


54

Hầu hết các vòng lặp sự kiện sẽ tạm dừng nếu không có sự kiện nào sẵn sàng, điều đó có nghĩa là hệ điều hành sẽ không cung cấp cho tác vụ bất kỳ thời gian thực hiện nào cho đến khi một sự kiện xảy ra.

Nói rằng sự kiện là một phím được nhấn. Bạn có thể hỏi nếu có một vòng lặp ở đâu đó trong hệ điều hành để kiểm tra các phím bấm. Câu trả lời là không. Các phím được nhấn tạo ra một ngắt , được xử lý không đồng bộ bởi phần cứng. Tương tự như vậy đối với bộ hẹn giờ, chuyển động chuột, gói đến, v.v.

Trong thực tế, đối với hầu hết các hệ điều hành, bỏ phiếu cho các sự kiện là sự trừu tượng. Phần cứng và HĐH xử lý các sự kiện không đồng bộ và đưa chúng vào hàng đợi có thể được thăm dò bởi các ứng dụng. Bạn chỉ thực sự thấy bỏ phiếu thực sự ở cấp phần cứng trong các hệ thống nhúng và thậm chí không phải lúc nào cũng vậy.


4
Không phải là một sự thay đổi điện áp trên dây? Điều đó có thể tự kích hoạt một sự kiện hay chúng ta phải thăm dò chân cho giá trị điện áp?
TheMeaningfulEngineer

8
Điều đó có thể tự kích hoạt một sự kiện. Bộ xử lý được thiết kế theo cách đó. Trong thực tế, một bộ xử lý có thể bị đánh thức khỏi giấc ngủ bởi một ngắt.
Karl Bielefeldt

2
Tôi bối rối với tuyên bố Most event loops will block. Làm thế nào điều này phù hợp với "mô hình vòng lặp sự kiện, trái ngược với việc sử dụng các luồng, sử dụng các cuộc gọi không đồng bộ không chặn"?
TheMeaningfulEngineer

1
Nếu bạn xem các vòng lặp sự kiện cho một thư viện như GTK +, họ sẽ kiểm tra các sự kiện mới sau đó gọi các trình xử lý sự kiện trong một vòng lặp, nhưng nếu không có bất kỳ sự kiện nào, chúng sẽ chặn trên semaphore hoặc bộ đếm thời gian hoặc thứ gì đó. Các nhà phát triển riêng lẻ tạo các vòng lặp sự kiện của riêng họ mà không chặn trên hàng đợi sự kiện trống, nhưng tất cả các thư viện được sử dụng rộng rãi đều chặn. Các vòng lặp sự kiện là quá kém hiệu quả.
Karl Bielefeldt

1
Tôi cho rằng bạn có thể nhìn theo cách đó, nhưng đó là đa luồng trong thư viện và / hoặc HĐH chứ không phải mã ứng dụng. Các vòng lặp sự kiện trừu tượng hóa các vấn đề đồng bộ hóa cho nhà phát triển ứng dụng.
Karl Bielefeldt

13

Tôi nghĩ về một người nghe sự kiện không phải là một chức năng chạy vòng lặp của riêng mình, mà là một cuộc đua tiếp sức với người chạy đầu tiên đang chờ khẩu súng khởi động. Một lý do quan trọng để sử dụng các sự kiện thay vì bỏ phiếu là chúng hiệu quả hơn với các chu kỳ CPU. Tại sao? Nhìn vào nó từ phần cứng trở lên (thay vì mã nguồn xuống).

Hãy xem xét một máy chủ Web. Khi máy chủ của bạn gọi listen()và chặn, mã của bạn sẽ được sử dụng như một máy chạy tiếp sức. Khi gói đầu tiên của kết nối mới đến, card mạng bắt đầu cuộc đua bằng cách làm gián đoạn hệ điều hành. HĐH chạy một thói quen dịch vụ ngắt (ISR) để lấy gói. ISR chuyển dùi cui đến một thói quen cấp cao hơn để thiết lập kết nối. Khi kết nối còn tồn tại, thói quen đó sẽ chuyển dùi cui đến listen(), nó chuyển dùi cui lên mã của bạn. Tại thời điểm đó, bạn có thể làm những gì bạn muốn với kết nối. Đối với tất cả những gì chúng ta biết, giữa các cuộc đua, mỗi người chạy tiếp sức có thể đi đến quán rượu. Một điểm mạnh của sự trừu tượng hóa là mã của bạn không cần phải biết hoặc không quan tâm.

Một số hệ điều hành bao gồm mã xử lý sự kiện chạy phần của cuộc đua, đưa dùi cui, sau đó quay trở lại điểm xuất phát của nó để chờ cuộc đua tiếp theo bắt đầu. Theo nghĩa đó, xử lý sự kiện được tối ưu hóa bỏ phiếu trong nhiều vòng lặp đồng thời. Tuy nhiên, luôn có một trình kích hoạt bên ngoài khởi động quá trình. Trình lắng nghe sự kiện không phải là một hàm không quay trở lại, mà là một hàm đang chờ kích hoạt bên ngoài đó trước khi nó chạy. Thay vì:

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

Tôi nghĩ về điều này như:

on(some event):    //I got the baton
     do stuff
     signal the next level up    //Pass the baton

và giữa signalvà lần tiếp theo trình xử lý chạy, về mặt khái niệm không có mã nào chạy hoặc lặp.


Điều này có ý nghĩa, tuy nhiên vòng lặp sự kiện đang làm gì trong khi nó đang chờ ngắt? Nếu nó bị chặn, đó không phải là cách tiếp cận 'đa luồng', mô hình ngược lại với vòng lặp sự kiện sao?
TheMeaningfulEngineer

Nếu mã của bạn có vòng lặp forever: { select(); do stuff; }thì bạn sẽ tham gia lại cuộc đua mỗi lần qua vòng lặp. Cho dù bạn làm điều đó nhiều lần từ một luồng hoặc song song trên các luồng hoặc bộ xử lý riêng biệt, tôi nghĩ mỗi sự kiện là cuộc đua riêng của nó. Ví dụ: trình duyệt Web là một chương trình đa luồng với nhiều vòng lặp sự kiện trong các luồng riêng biệt, ít nhất một cho UI và một cho mỗi trang mà nó đang tải xuống. Câu hỏi tôi đặt ra khi viết mã là "làm thế nào tôi có thể xử lý các sự kiện đủ nhanh?" Đôi khi câu trả lời là một vòng lặp, đôi khi chủ đề, thường là sự kết hợp.
cxw

Tôi đã làm lập trình dựa trên sự kiện trong hơn một vài thập kỷ và tôi thấy câu trả lời này rất khó hiểu. Để một người nghe làm việc, phải có một vòng lặp chờ đợi một sự kiện, sau đó định tuyến nó đến tất cả những người nghe đã đăng ký. (Giả sử chúng ta đang nói về phần mềm thay vì ngắt phần cứng)
Bryan Oakley

8

Không. Nó không phải là "bỏ phiếu tối ưu hóa." Một vòng lặp sự kiện sử dụng I / O điều khiển ngắt thay vì bỏ phiếu.

Vòng lặp While, Until, For, v.v. là vòng lặp bỏ phiếu.

"Bỏ phiếu" là quá trình kiểm tra nhiều lần. Vì mã vòng lặp thực thi liên tục và đây là một vòng lặp nhỏ, "chặt chẽ", nên có ít thời gian để bộ xử lý chuyển đổi tác vụ và làm bất cứ điều gì khác. Hầu như tất cả "treo", "đóng băng", "khóa máy" hoặc bất cứ điều gì bạn muốn gọi nó khi máy tính không phản hồi, là biểu hiện của mã bị kẹt trong một vòng bỏ phiếu ngoài ý muốn. Thiết bị sẽ hiển thị 100% sử dụng CPU.

Các vòng lặp sự kiện điều khiển gián đoạn có hiệu quả hơn nhiều so với các vòng bỏ phiếu. Bỏ phiếu là việc sử dụng chu kỳ CPU cực kỳ lãng phí, vì vậy mọi nỗ lực được thực hiện để loại bỏ hoặc giảm thiểu nó.

Tuy nhiên, để tối ưu hóa chất lượng mã, hầu hết các ngôn ngữ đều cố gắng sử dụng mô hình vòng lặp bỏ phiếu càng sát càng tốt cho các lệnh xử lý sự kiện vì chúng phục vụ các mục đích tương tự về chức năng trong một chương trình. Do đó, với việc bỏ phiếu là cách quen thuộc hơn để chờ nhấn phím hoặc thứ gì đó, thật dễ dàng cho người thiếu kinh nghiệm sử dụng nó và kết thúc với một chương trình có thể tự chạy tốt, nhưng không có gì khác hoạt động trong khi nó chạy. Nó đã "tiếp quản" máy.

Như đã giải thích trong các câu trả lời khác, trong quá trình xử lý sự kiện bị gián đoạn, về cơ bản, một "cờ" được đặt trong CPU và quá trình này bị "treo" (không được phép chạy) cho đến khi cờ đó bị thay đổi bởi một số quy trình khác (chẳng hạn như bàn phím trình điều khiển thay đổi nó khi người dùng đã nhấn một phím). Nếu cờ là một điều kiện phần cứng thực tế, chẳng hạn như một dòng được "kéo lên cao", thì nó được gọi là "ngắt" hoặc "ngắt phần cứng". Tuy nhiên, hầu hết, được triển khai như một địa chỉ bộ nhớ trên CPU hoặc trong bộ nhớ chính (RAM) và được gọi là "semaphores".

Semaphores có thể được thay đổi dưới sự kiểm soát của phần mềm và do đó có thể cung cấp một cơ chế báo hiệu đơn giản, rất nhanh giữa các quy trình phần mềm.

Ngắt, tuy nhiên, chỉ có thể được thay đổi bởi phần cứng. Việc sử dụng phổ biến nhất của các ngắt là một được kích hoạt đều đặn bởi chip đồng hồ bên trong. Một trong vô số các hành động phần mềm được kích hoạt bởi các ngắt đồng hồ, là việc thay đổi các từ ngữ nghĩa.

Tôi đã bỏ đi rất nhiều nhưng phải dừng lại ở đâu đó. Xin hỏi nếu bạn cần thêm chi tiết.


7

Thông thường, câu trả lời là phần cứng, hệ điều hành và các luồng nền mà bạn không kiểm soát âm mưu để làm cho nó trông dễ dàng. Card mạng nhận được một số dữ liệu mà nó tạo ra một ngắt để cho CPU biết. Trình xử lý ngắt của HĐH xử lý nó. Sau đó, một luồng nền mà bạn không kiểm soát (được tạo bằng cách đăng ký sự kiện và đã ngủ kể từ khi bạn đăng ký sự kiện) được HĐH đánh thức như một phần của việc xử lý sự kiện và chạy trình xử lý sự kiện của bạn.


7

Tôi sẽ đi ngược lại tất cả các câu trả lời khác mà tôi thấy cho đến nay và nói "có" . Tôi nghĩ rằng các câu trả lời khác đang làm phức tạp mọi thứ quá nhiều. Từ quan điểm khái niệm, tất cả các vòng lặp sự kiện về cơ bản là:

while <the_program_is_running> {
    event=wait_for_next_event()
    process_event(event)
}

Nếu bạn đang cố gắng hiểu các vòng lặp sự kiện lần đầu tiên, nghĩ về chúng như một vòng lặp đơn giản sẽ không có hại. Một số khung cơ bản đang chờ HĐH phân phối một sự kiện, sau đó định tuyến sự kiện đến một hoặc nhiều trình xử lý, sau đó chờ sự kiện tiếp theo, v.v. Đó thực sự là tất cả những gì có từ góc độ phần mềm ứng dụng.


1
+1 vì đơn giản. Nhưng trọng tâm là "chờ đợi"; thực thi mã này sẽ DỪNG trên dòng đầu tiên của vòng lặp và không tiếp tục cho đến khi một sự kiện xảy ra. Điều này khác với một vòng bỏ phiếu bận rộn liên tục kiểm tra một sự kiện cho đến khi xảy ra.
Tom Panning

1

Không phải tất cả các kích hoạt sự kiện được xử lý trong các vòng lặp. Cách tôi thường viết các công cụ sự kiện của riêng mình sẽ như thế này:

interface Listener {
    void handle (EventInfo info);
}

List<Listener> registeredListeners

void triggerEvent (EventInfo info) {
    foreach (listener in registeredListeners) { // Memo 1
        listener.handle(info) // the handling may or may not be synchronous... your choice
    }
}

void somethingThatTriggersAnEvent () {
    blah
    blah
    blah
    triggerEvent(someGeneratedEventInfo)
    more blah
}

Lưu ý rằng mặc dù Bản ghi nhớ 1 nằm trên một vòng lặp, nhưng vòng lặp này là để thông báo cho mỗi người nghe. Kích hoạt sự kiện chính nó không nhất thiết phải trong một vòng lặp.

Về lý thuyết, các sự kiện quan trọng ở cấp độ hệ điều hành có thể sử dụng cùng một kỹ thuật (mặc dù tôi nghĩ rằng chúng thường thực hiện bỏ phiếu thay thế? Tôi chỉ đang suy đoán ở đây), với điều kiện HĐH sẽ tiết lộ một số loại registerListenerAPI.

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.