나는 투표를 좋아한다! 내가합니까? 예! 내가합니까? 예! 내가합니까? 예! 아직도 그래요? 예! 지금은 어때? 예!
다른 사람들이 언급했듯이, 동일한 변경되지 않은 상태를 반복해서 되찾기 위해 폴링하는 경우 엄청나게 비효율적 일 수 있습니다. 이는 CPU 사이클을 태우고 모바일 장치에서 배터리 수명을 크게 단축시키는 방법입니다. 물론 매번 원하는만큼 빠른 속도로 새롭고 의미있는 상태를 되 찾는다면 낭비가되지 않습니다.
그러나 폴링을 좋아하는 주된 이유는 단순성과 예측 가능한 특성 때문입니다. 코드를 추적하여 언제 어디서 어떤 일이 일어날 지 쉽게 볼 수 있습니다. 이론적으로 우리가 폴링이 무시할만한 폐기물이었던 세계에 살았다면 (실제로는 멀지 만), 코드를 유지하는 것은 엄청나게 큰 일이라고 생각합니다. 이 경우에는 안되지만 성능을 무시할 수 있는지 알 수 있듯이 폴링 및 풀링의 이점입니다.
DOS 시대에 프로그래밍을 시작했을 때 내 작은 게임은 폴링을 중심으로 진행되었습니다. 키보드 인터럽트와 관련하여 간신히 이해 한 책에서 일부 어셈블리 코드를 복사하여 키보드 루프를 저장했습니다.이 시점에서 메인 루프는 항상 폴링되었습니다. 업 키가 다운입니까? 아니. 업 키가 다운입니까? 아니. 지금은 어때? 아니. 지금? 예. 좋아, 선수를 움직여
그리고 엄청나게 낭비 적이지만, 오늘날의 멀티 태스킹 및 이벤트 중심 프로그래밍에 비해 추론하기가 훨씬 쉽다는 것을 알았습니다. 나는 언제 어디서 일어날 지 정확히 알았으며 딸꾹질없이 프레임 속도를 안정적이고 예측 가능하게 유지하는 것이 더 쉬웠습니다.
그래서 그때부터 나는 항상 조건을 사용하여 스레드가 새 상태를 가져올 수있는 시점을 깨우도록 알리는 조건 변수를 사용하는 것과 같이 실제로 CPU 사이클을 태우지 않고 그 이점과 예측 가능성을 얻는 방법을 찾으려고 노력해 왔습니다. 그들의 일을하고 다시 알림을 받기 위해 다시 잠 들으십시오.
그리고 어쨌든 이벤트 큐는 관찰자 패턴보다 작업하기가 훨씬 쉽다는 것을 알았습니다. 여전히 어디로 갈지 또는 어떤 일이 일어날 지 예측하기가 쉽지는 않습니다. 최소한 이벤트 처리 제어 흐름을 시스템의 몇 가지 주요 영역으로 중앙 집중화하고 중앙 이벤트 처리 스레드 외부에서 갑자기 하나의 기능에서 완전히 먼 곳으로 튀어 나오는 대신 동일한 스레드에서 해당 이벤트를 처리합니다. 따라서 이분법이 항상 관찰자와 폴링 사이에있을 필요는 없습니다. 이벤트 대기열은 중간 정도입니다.
그러나 그렇습니다. 어쨌든 필자는 이전에 폴링 할 때 사용했던 예측 가능한 제어 흐름과 유사하게 작동하는 시스템에 대해 추론하기가 훨씬 쉽다는 것을 알았습니다. 상태 변경이 발생하지 않은 시간. 따라서 조건 변수처럼 불필요하게 CPU 사이클을 태우지 않는 방식으로 할 수 있다면 이점이 있습니다.
동종 루프
좋아, 나는 Josh Caswell
내 대답에 약간의 바보가 있음을 지적한 큰 의견을 얻었다 .
"스레드가 깨어나도록 알리기 위해 조건 변수를 사용하는 것과 같음"폴링이 아닌 이벤트 기반 / 관찰자 배열처럼 들립니다
기술적으로 조건 변수 자체는 관찰자 패턴을 적용하여 스레드를 깨우거나 알리므로 "폴링"이 아마도 오도를 불러 일으킬 수 있습니다. 그러나 DOS 시절의 폴링과 비슷한 이점을 제공합니다 (제어 흐름 및 예측 가능성 측면에서). 나는 그것을 더 잘 설명하려고 노력할 것이다.
당시에 내가 찾은 것은 코드 섹션을 보거나 추적 할 수 있다는 것입니다. "이 섹션 전체는 키보드 이벤트 처리에 전념하고 있습니다.이 코드 섹션에서는 다른 일이 일어나지 않을 것입니다. 그리고 나는 앞으로 무슨 일이 일어날 지 정확히 알고 있으며, 이후에 일어날 일을 정확히 알고 있습니다 (예 : 물리와 렌더링). " 키보드 상태의 폴링은이 외부 이벤트에 대한 응답을 처리하는 한 제어 흐름의 중앙 집중화를 제공합니다. 이 외부 이벤트에 즉시 응답하지 않았습니다. 우리는 편의에 응답했습니다.
옵저버 패턴을 기반으로하는 푸시 기반 시스템을 사용하면 종종 이러한 이점을 잃게됩니다. 크기 조정 이벤트를 트리거하는 컨트롤 크기가 조정될 수 있습니다. 우리가 그것을 추적 할 때, 우리는 더 많은 이벤트를 유발하는 크기 조정에서 많은 사용자 정의 작업을 수행하는 이국적인 컨트롤 안에 있습니다. 우리는 시스템에서 어디에서 발생하는지에 관한 모든 계단식 사건을 추적하는 것에 완전히 놀랐습니다. 또한 스레드 A가 여기에서 컨트롤의 크기를 조정할 수 있지만 스레드 B도 나중에 컨트롤의 크기를 조정하기 때문에 주어진 스레드에서이 모든 것이 일관되게 발생하지 않을 수도 있습니다. 그래서 나는 항상 모든 일이 어디에서 일어날 지 예측하는 것이 얼마나 어려운지에 대해 추론하기가 매우 어렵다는 것을 알았습니다.
이벤트 큐는 스레드 스레드에서 이러한 모든 일이 발생하는 위치를 단순화하기 때문에 추론하기가 조금 더 간단합니다. 그러나 많은 다른 일들이 일어날 수 있습니다. 이벤트 큐에는 처리 할 다양한 이벤트가 포함될 수 있으며 각 이벤트는 발생하는 일련의 이벤트, 처리 된 순서 및 코드베이스의 모든 위치에서 수신 거부되는 방식에 대해 여전히 우리를 놀라게 할 수 있습니다. .
폴링에 "가장 가까운"것으로 생각하는 것은 이벤트 큐를 사용하지 않지만 매우 동종 유형의 처리를 지연시킵니다. A는 PaintSystem
이 적절한 z 순서에서 내부 세포 페인트 다시 모든 것을 통해 간단한 연속 루프를 수행 지적하는 윈도우의 특정 그리드 셀을 다시 칠하기 위해 할 일이이 그림 있다는 조건 변수를 통해 경고 할 수 있습니다. 여기에 다시 페인트해야 할 셀에 상주하는 각 위젯에서 페인트 이벤트를 트리거하기 위해 간접 / 동적 디스패치 호출 레벨이 하나있을 수 있지만, 이것이 바로 하나의 간접 호출 계층입니다. 조건 변수는 관찰자 패턴을 사용하여 PaintSystem
수행해야한다고 경고 하지만 그 이상을 지정하지는 않습니다.PaintSystem
그 시점에서 하나의 균일하고 매우 균질 한 작업에 전념합니다. PaintSystem's
코드 를 디버깅하고 추적 할 때 페인팅 이외의 다른 작업은 발생하지 않습니다.
따라서 이벤트 큐 처리에서 얻을 수있는 수많은 책임을 수행하는 이종 유형의 데이터에 대해 비균질 루프 대신 데이터에 대해 균일 한 루프를 수행하는 데이터를 대상으로 균질 루프를 수행하는 위치로 시스템을 이동시키는 것이 대부분입니다.
우리는 이런 종류의 것을 목표로하고 있습니다 :
when there's work to do:
for each thing:
apply a very specific and uniform operation to the thing
반대로 :
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.
기타 등등. 그리고 작업 당 하나의 스레드 일 필요는 없습니다. 하나의 스레드는 GUI 컨트롤에 레이아웃 (크기 조정 / 위치 변경) 논리를 적용하고 다시 칠할 수 있지만 키보드 나 마우스 클릭을 처리하지 못할 수 있습니다. 따라서 이것을 이벤트 큐의 동질성을 향상시키는 것으로 볼 수 있습니다. 그러나 이벤트 큐를 사용하거나 인터리브 크기 조정 및 페인팅 기능을 사용할 필요는 없습니다. 우리는 다음과 같이 할 수 있습니다 :
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
따라서 위의 접근 방식은 조건 변수를 사용하여 수행 할 작업이있을 때 스레드에 알리지 만 다른 유형의 이벤트 (한 루프의 크기 조정, 다른 루프의 페인트, 둘의 혼합이 아닌 페인트)를 인터리브하지 않습니다. • 정확히 수행해야 할 작업 (스레드가 시스템 전체 ECS 상태를보고 깨어 난 스레드를 "발견")을 전달하지 않아도됩니다. 수행하는 각 루프는 본질적으로 매우 균일하므로 모든 일이 발생하는 순서를 쉽게 추론 할 수 있습니다.
이 유형의 접근 방식을 무엇으로 해야할지 모르겠습니다. 나는 다른 GUI 엔진이 이것을하는 것을 보지 못했고 그것은 내 자신의 이국적인 접근 방식입니다. 그러나 옵저버 또는 이벤트 큐를 사용하여 멀티 스레드 GUI 프레임 워크를 구현하려고 시도하기 전에 디버깅이 엄청나게 어려웠으며 자신감이 가지 않는 방식으로 해결하기에 충분하지 않은 모호한 경쟁 조건과 교착 상태가 발생했습니다. 해결책에 대해 (일부 사람들은 이것을 할 수 있지만 충분히 똑똑하지는 않습니다). 첫 번째 반복 디자인은 신호를 통해 직접 슬롯을 호출했으며 일부 슬롯은 비동기 작업을 수행하기 위해 다른 스레드를 생성했을 것입니다. 두 번째 반복은 이벤트 큐를 사용했으며 추론하기가 조금 더 쉬웠습니다. 하지만 난잡한 교착 상태와 경쟁 상황에 빠지지 않고 내 두뇌가 할 수있을만큼 쉽지는 않다. 세 번째 및 마지막 반복은 위에서 설명한 접근 방식을 사용했으며 마침내 저와 같은 바보조차 올바르게 구현 할 수있는 다중 스레드 GUI 프레임 워크를 만들 수있었습니다.
그런 다음이 유형의 최종 멀티 스레드 GUI 디자인을 사용하면 추론하기 쉬운 유형의 실수를 피하고 내가 저지른 실수를 피할 수있는 다른 것을 생각 해낼 수있었습니다. 최소한 균등 한 루프와 DOS 시절에 폴링 할 때와 비슷한 제어 흐름과 비슷한 방식 때문입니다 (실제로 폴링하지 않고 수행해야 할 작업 만 수행하더라도). 비균질 루프, 비균질 부작용, 비균질 제어 흐름을 의미하고 균일 한 데이터에서 균일하게 작동하고 격리되는 동종 루프를 향해 점점 더 많은 작업을 수행하는 것이 가능한 한 이벤트 처리 모델에서 멀어지게하는 것이 었습니다. "무엇"에 더 쉽게 집중할 수있는 방식으로 부작용을 통합