Tôi thích bỏ phiếu! Tôi phải không Vâng! Tôi phải không Vâng! Tôi phải không Vâng! Tôi còn không Vâng! Bây giờ thì sao? Vâng!
Như những người khác đã đề cập, nó có thể cực kỳ kém hiệu quả nếu bạn bỏ phiếu chỉ để nhận lại trạng thái không thay đổi lặp đi lặp lại. Đó là một công thức để đốt cháy chu kỳ CPU và rút ngắn đáng kể thời lượng pin trên thiết bị di động. Tất nhiên sẽ không lãng phí nếu bạn trở lại trạng thái mới và có ý nghĩa mỗi lần với tốc độ không nhanh hơn mong muốn.
Nhưng lý do chính khiến tôi thích bỏ phiếu là vì tính đơn giản và tính dễ đoán của nó. Bạn có thể theo dõi thông qua mã và dễ dàng xem khi nào và nơi mọi thứ sẽ xảy ra, và trong chuỗi nào. Nếu, về mặt lý thuyết, chúng ta sống trong một thế giới mà việc bỏ phiếu là một sự lãng phí không đáng kể (mặc dù thực tế cách xa nó), thì tôi tin rằng nó sẽ đơn giản hóa việc duy trì mã rất nhiều. Và đó là lợi ích của việc bỏ phiếu và kéo như tôi thấy nếu chúng ta có thể coi thường hiệu suất, mặc dù chúng ta không nên trong trường hợp này.
Khi tôi bắt đầu lập trình trong kỷ nguyên DOS, các trò chơi nhỏ của tôi xoay quanh việc bỏ phiếu. Tôi đã sao chép một số mã lắp ráp từ một cuốn sách mà tôi hầu như không hiểu liên quan đến các ngắt bàn phím và làm cho nó lưu trữ một bộ đệm của các trạng thái bàn phím, tại đó vòng lặp chính của tôi luôn luôn bỏ phiếu. Là phím lên xuống? Không. Là phím lên xuống? Không. Làm thế nào bây giờ? Không. Hiện nay? Vâng. Được rồi, di chuyển người chơi.
Và trong khi vô cùng lãng phí, tôi thấy rằng lý do dễ dàng hơn nhiều so với những ngày lập trình đa tác vụ và hướng sự kiện. Tôi biết chính xác thời gian và mọi thứ sẽ xảy ra mọi lúc và dễ dàng hơn để giữ tốc độ khung hình ổn định và có thể dự đoán được mà không có trục trặc.
Vì vậy, kể từ đó, tôi đã luôn cố gắng tìm cách để có được một số lợi ích và dự đoán về điều đó mà không thực sự đốt cháy chu kỳ CPU, như sử dụng các biến điều kiện để thông báo cho các luồng để đánh thức trạng thái mới, tại đó chúng có thể kéo trạng thái mới, làm việc của họ, và quay trở lại giấc ngủ để được thông báo lại.
Và bằng cách nào đó tôi thấy hàng đợi sự kiện dễ dàng hơn rất nhiều để làm việc với ít nhất là các mẫu quan sát, mặc dù chúng vẫn không dễ dự đoán nơi bạn sẽ đến hoặc điều gì sẽ xảy ra. Họ ít nhất tập trung luồng điều khiển xử lý sự kiện đến một vài khu vực chính trong hệ thống và luôn xử lý các sự kiện đó trong cùng một luồng thay vì chuyển từ một chức năng sang một nơi hoàn toàn xa xôi và bất ngờ bên ngoài luồng xử lý sự kiện trung tâm. Vì vậy, sự phân đôi không phải luôn luôn nằm giữa người quan sát và bỏ phiếu. Hàng đợi sự kiện là một loại trung gian ở đó.
Nhưng vâng, bằng cách nào đó tôi thấy dễ dàng hơn nhiều để lý luận về các hệ thống thực hiện những thứ gần giống với loại luồng kiểm soát dự đoán mà tôi từng có khi tôi bỏ phiếu từ lâu, trong khi chỉ chống lại xu hướng công việc xảy ra tại lần khi không có thay đổi trạng thái đã xảy ra. Vì vậy, sẽ có lợi nếu bạn có thể làm điều đó theo cách không đốt cháy chu kỳ CPU một cách không cần thiết như với các biến điều kiện.
Vòng lặp đồng nhất
Được rồi, tôi đã nhận được một nhận xét tuyệt vời từ Josh Caswell
đó chỉ ra một số sự ngu ngốc trong câu trả lời của tôi:
"như sử dụng các biến điều kiện để thông báo cho các chủ đề thức dậy" Âm thanh giống như sự sắp xếp dựa trên sự kiện / người quan sát, không bỏ phiếu
Về mặt kỹ thuật, chính biến điều kiện đang áp dụng mô hình quan sát để đánh thức / thông báo các luồng, do đó, để gọi rằng "bỏ phiếu" có thể sẽ gây hiểu nhầm đáng kinh ngạc. Nhưng tôi thấy nó mang lại một lợi ích tương tự mà tôi đã tìm thấy khi bỏ phiếu từ thời DOS (chỉ xét về lưu lượng kiểm soát và khả năng dự đoán). Tôi sẽ cố gắng giải thích nó tốt hơn.
Điều tôi thấy hấp dẫn trở lại trong những ngày đó là bạn có thể nhìn vào một phần của mã hoặc truy tìm nó và nói: "Được rồi, toàn bộ phần này được dành riêng để xử lý các sự kiện bàn phím. Không có gì khác sẽ xảy ra trong phần mã này Và tôi biết chính xác những gì sẽ xảy ra trước đó và tôi biết chính xác những gì sẽ xảy ra sau đó (vật lý và kết xuất, ví dụ). " Việc bỏ phiếu của các trạng thái bàn phím đã cho bạn loại tập trung của luồng điều khiển cho đến khi xử lý những gì sẽ diễn ra để đáp ứng với sự kiện bên ngoài này. Chúng tôi đã không đáp ứng với sự kiện bên ngoài này ngay lập tức. Chúng tôi trả lời nó một cách thuận tiện.
Khi chúng tôi sử dụng hệ thống dựa trên đẩy dựa trên mẫu Người quan sát, chúng tôi thường mất những lợi ích đó. Một điều khiển có thể được thay đổi kích thước mà kích hoạt một sự kiện thay đổi kích thước. Khi chúng tôi theo dõi thông qua nó, chúng tôi thấy chúng tôi nằm trong sự kiểm soát kỳ lạ, có rất nhiều điều tùy chỉnh trong việc thay đổi kích thước của nó sẽ kích hoạt nhiều sự kiện hơn. Chúng tôi cuối cùng đã hoàn toàn bất ngờ truy tìm tất cả các sự kiện xếp tầng này như là nơi chúng tôi kết thúc trong hệ thống. Hơn nữa, chúng ta có thể thấy rằng tất cả những điều này thậm chí không xảy ra nhất quán trong bất kỳ luồng đã cho nào bởi vì luồng A có thể thay đổi kích thước điều khiển ở đây trong khi luồng B cũng thay đổi kích thước điều khiển sau này. Vì vậy, tôi luôn thấy điều này rất khó để suy luận về việc khó dự đoán mọi thứ xảy ra cũng như những gì sẽ xảy ra.
Lý do hàng đợi sự kiện đơn giản hơn một chút đối với tôi vì nó đơn giản hóa nơi tất cả những điều này xảy ra ít nhất ở cấp độ luồng. Tuy nhiên, nhiều điều khác nhau có thể xảy ra. Một hàng đợi sự kiện có thể chứa một hỗn hợp các sự kiện chiết trung để xử lý và mỗi người vẫn có thể làm chúng ta ngạc nhiên về dòng thác các sự kiện đã xảy ra, thứ tự chúng được xử lý và cách chúng ta kết thúc ở mọi nơi trong cơ sở mã .
Những gì tôi đang xem xét "gần nhất" với việc bỏ phiếu sẽ không sử dụng hàng đợi sự kiện nhưng sẽ trì hoãn một kiểu xử lý rất đồng nhất. Người PaintSystem
ta có thể được cảnh báo thông qua một biến điều kiện có công việc vẽ để làm lại một số ô lưới nhất định của cửa sổ, tại đó, nó thực hiện một vòng tuần tự đơn giản thông qua các ô và sơn lại mọi thứ bên trong nó theo thứ tự z thích hợp. Có thể có một cấp gọi điện thoại gián tiếp / công văn động ở đây để kích hoạt các sự kiện sơn trong mỗi tiện ích nằm trong một ô cần được sơn lại, nhưng đó chỉ là một lớp của các cuộc gọi gián tiếp. Biến điều kiện sử dụng mẫu quan sát viên để cảnh báo PaintSystem
rằng nó có công việc phải làm, nhưng nó không chỉ định gì hơn thế vàPaintSystem
được dành cho một đồng phục, nhiệm vụ rất đồng nhất tại thời điểm đó. Khi chúng tôi gỡ lỗi và truy tìm PaintSystem's
mã, chúng tôi biết rằng sẽ không có gì khác xảy ra ngoài việc vẽ.
Vì vậy, chủ yếu là về việc đưa hệ thống xuống nơi bạn có những điều này thực hiện các vòng lặp đồng nhất đối với dữ liệu áp dụng một trách nhiệm rất đơn lẻ đối với nó thay vì các vòng lặp không đồng nhất đối với các loại dữ liệu khác nhau thực hiện nhiều trách nhiệm như chúng ta có thể xử lý hàng đợi sự kiện.
Chúng tôi đang nhắm đến loại điều này:
when there's work to do:
for each thing:
apply a very specific and uniform operation to the thing
Như trái ngược với:
when one specific event happens:
do something with relevant thing
in relevant thing's event:
do some more things
in thing1's triggered by thing's event:
do some more things
in thing2's event triggerd by thing's event:
do some more things:
in thing3's event triggered by thing2's event:
do some more things
in thing4's event triggered by thing1's event:
cause a side effect which shouldn't be happening
in this order or from this thread.
Và kể từ đó trở đi. Và nó không phải là một chủ đề cho mỗi nhiệm vụ. Một luồng có thể áp dụng logic bố cục (thay đổi kích thước / định vị lại) cho các điều khiển GUI và sơn lại chúng, nhưng nó có thể không xử lý các lần nhấp chuột hoặc bàn phím. Vì vậy, bạn có thể xem điều này như chỉ cải thiện tính đồng nhất của hàng đợi sự kiện. Nhưng chúng ta không phải sử dụng hàng đợi sự kiện và xen kẽ các chức năng thay đổi kích thước và vẽ. Chúng ta có thể làm như:
in thread dedicated to layout and painting:
when there's work to do:
for each widget that needs resizing/reposition:
resize/reposition thing to target size/position
mark appropriate grid cells as needing repainting
for each grid cell that needs repainting:
repaint cell
go back to sleep
Vì vậy, cách tiếp cận trên chỉ sử dụng một biến điều kiện để thông báo cho luồng khi có việc cần làm, nhưng nó không xen kẽ các loại sự kiện khác nhau (thay đổi kích thước trong một vòng lặp, vẽ trong một vòng lặp khác, không phải là hỗn hợp của cả hai) và nó không ' không bận tâm để truyền đạt những gì công việc chính xác cần phải được thực hiện (chủ đề "phát hiện ra" khi thức dậy bằng cách nhìn vào trạng thái toàn hệ thống của ECS). Mỗi vòng lặp mà nó thực hiện sau đó rất đồng nhất về bản chất, giúp dễ dàng suy luận về thứ tự mà mọi thứ xảy ra.
Tôi không chắc nên gọi kiểu tiếp cận này là gì. Tôi chưa thấy các công cụ GUI khác làm điều này và đó là cách tiếp cận kỳ lạ của riêng tôi đối với tôi. Nhưng trước đây khi tôi cố gắng triển khai các khung GUI đa luồng bằng cách sử dụng các trình quan sát hoặc hàng đợi sự kiện, tôi đã gặp khó khăn rất lớn trong việc gỡ lỗi và cũng gặp phải một số điều kiện cuộc đua tối nghĩa và những bế tắc mà tôi không đủ thông minh để khắc phục theo cách khiến tôi cảm thấy tự tin về giải pháp (một số người có thể làm điều này nhưng tôi không đủ thông minh). Thiết kế lặp đầu tiên của tôi chỉ gọi một vị trí trực tiếp thông qua tín hiệu và một số vị trí sau đó sẽ sinh ra các luồng khác để thực hiện công việc không đồng bộ, và đó là lý do khó nhất để giải quyết và tôi đã vấp ngã trong điều kiện cuộc đua và bế tắc. Lặp lại thứ hai đã sử dụng một hàng đợi sự kiện và đó là lý do dễ dàng hơn một chút về, nhưng không đủ dễ dàng để bộ não của tôi làm điều đó mà không phải chạy vào bế tắc tối nghĩa và điều kiện chủng tộc. Lặp lại thứ ba và cuối cùng đã sử dụng cách tiếp cận được mô tả ở trên và cuối cùng cho phép tôi tạo ra một khung GUI đa luồng mà ngay cả một người đơn giản ngu ngốc như tôi cũng có thể thực hiện chính xác.
Sau đó, kiểu thiết kế GUI đa luồng cuối cùng này cho phép tôi đưa ra một thứ khác dễ hiểu hơn nhiều và tránh những loại lỗi mà tôi có xu hướng mắc phải, và một trong những lý do khiến tôi thấy nó dễ dàng hơn rất nhiều ít nhất là do các vòng lặp đồng nhất này và cách chúng giống với luồng điều khiển tương tự như khi tôi bỏ phiếu trong những ngày DOS (mặc dù nó không thực sự bỏ phiếu và chỉ thực hiện công việc khi hoàn thành công việc). Ý tưởng là di chuyển càng xa mô hình xử lý sự kiện càng tốt, trong đó ngụ ý các vòng lặp không đồng nhất, tác dụng phụ không đồng nhất, các luồng điều khiển không đồng nhất và hoạt động nhiều hơn đối với các vòng lặp đồng nhất hoạt động thống nhất trên dữ liệu đồng nhất và cách ly và thống nhất các tác dụng phụ theo những cách giúp dễ dàng hơn chỉ tập trung vào "cái gì"