상태 머신 대 스레드


24

Alan Cox "컴퓨터는 상태 머신입니다. 스레드는 상태 머신을 프로그래밍 할 수없는 사람들을위한 것"이라고 말했습니다.
Alan에게 직접 물어 보는 것은 겸손한 선택이 아니기 때문에 여기서 묻고 싶습니다. 하나의 스레드와 상태 머신 만 사용하여 Java와 같은 고급 언어의 멀티 스레딩 기능을 어떻게 구현할 수 있습니까? 예를 들어, 수행해야 할 두 가지 활동 (계산 및 I / O 수행)이 있고 하나의 활동이 차단 될 수 있다면 어떨까요?
고급 언어에서 멀티 스레딩을 대체 할 수있는 "상태 머신 전용"방식을 사용하고 있습니까?


4
컴퓨터도 튜링 머신입니다. 그럼에도 불구하고 튜링 머신처럼 프로그래밍하는 것이 반드시 유용한 것은 아닙니다. 명령형 언어로 된 스택은 매우 유용한 기능이며 멀티 스레드 프로그램을 통해 여러 스택을 동시에 메모리에 유지할 수 있습니다. 상태 머신에서 동일한 작업을 수행하는 것이 가능하지만 더 복잡합니다.
thiton

10
Alan은 OS 커널 개발자였습니다. 이 사람은 그의 지배인이었다 . 따라서 그의 인용문은 그 맥락에서 취해야합니다. 그는 그러한 모델을 사용하는 것이 더 적절한 '금속에 대하여'프로그래밍하고 있었을 것이다. OS가 하드웨어를 추상화하고 그 자체의 고유 속성 ( "컴퓨터는 상태 머신 ...")을 선택하면 도메인 에서 더 적합한 다른 모델을 사용할 수있는 기회와 이점이 있습니다 . 거의 모든 게임은 상태 머신을 많이 사용합니다.
Steven Evers

2
스레딩은 원하는 경우 일부 상태 머신 스위치를 자동으로 관리하는 OS의 기능입니다. 분명히 모든 것을 자체적으로 관리하는 거대한 상태 머신을 만들 수는 있지만 더 복잡합니다. 프로세스에 대해서도 마찬가지입니다. 프로세스는 상태 머신을 프로그래밍 할 수없는 사람들을위한 것이라고 말할 수 있습니다. 그러나 추상화는 훨씬 간단하고 오류가 적은 인터페이스를 제공합니다. 내 생각에 이것은 단지 듣고 생각하고 현실에서 무시해야 할 또 다른 "멋진 인용구"일 ​​뿐이다.
얌 마르코비치

2
"하지만 [스레드] 추상화는 훨씬 간단하고 오류가 적은 인터페이스를 제공합니다." 그건 잘못된 것 같습니다. 스레드 안전성이 잘못된 사람들의 수는 오류를 일으키는 것으로 나타납니다.
S.Lott

1
여기의 많은 의견과 답변은 일반적으로 멀티 태스킹이 아닌 것으로 해석합니다. Alan Cox는 단순히 스레드 방지이며 사람들이 스레드를 사용하는 많은 목표를 달성하기 위해 여러 프로세스를 사용하도록 옹호합니다. 그는 포크 FTW라는 유닉스 해커입니다. 인용문에서 직접 그에 대한 의견을 찾지 못했지만 다음은 Linux 커널 메일 링리스트의 Larry McVoy의 의견입니다
Martin B

답변:


25

스레드의 모든 작업은 인터리브 작업이므로 프로세스의 일부가 시간이 겹치는 것처럼 보입니다. 여러 스레드가있는 단일 코어 시스템은 한 번의 스레드에서 작은 비트의 코드를 실행 한 다음 다른 스레드로 전환하기 만합니다. 간단한 스케줄러는 우선 순위가 가장 높은 스레드를 결정하고 실제로 코어에서 실행됩니다.

단일 코어 컴퓨터에서는 실제로 "동시에" 아무 것도 발생 하지 않습니다 . 모두 인터리브 된 실행입니다.

인터리빙을 달성하는 방법은 여러 가지가 있습니다. 많은.

두 스레드가 공통 변수에 쓸 수 있도록 간단한 잠금을 사용하는 간단한 두 스레드 프로세스가 있다고 가정 해 봅시다. 6 개의 코드 블록이 있습니다.

  • 잠금 전 T1
  • 잠금 장치가있는 T1
  • T1 애프터 락
  • 잠금 전 T2
  • 잠금 장치가있는 T2
  • T2 애프터 락

[이것은 루프에 있거나 더 많은 잠금 장치 또는 기타가있을 수 있습니다. 더 복잡하지 않고 더 길어집니다.]

T1 단계는 순서대로 (T1- 전, T1-with, T1- 후) 실행해야하고 T2 단계는 순서로 (T2- 전, T2-with, T2- 후) 실행해야합니다.

"순서대로"제약 조건 이외의 방법으로 인터리브 할 수 있습니다. 어쨌든. 위에 나열된대로 실행할 수 있습니다. 다른 유효한 순서는 (T1- 전, T2- 전, T2- 잠금, T1- 잠금, T2- 후, T1- 후)입니다. 유효한 주문이 많이 있습니다.

기다림.

이것은 6 개의 상태를 가진 상태 머신 일뿐입니다.

비 결정적 유한 상태 오토마타입니다. T2-xxx 상태와 T1-xxx 상태의 순서는 불확실하며 중요하지 않습니다. 따라서 "다음 상태"는 동전 던지기 장소입니다.

예를 들어, FSM이 시작될 때 T1- 이전 또는 T2- 이전은 모두 합법적 인 첫 번째 상태입니다. 동전을 던지세요.

T1 이전에 등장했다고 가정 해 봅시다. 그렇게. 완료되면 T1-with와 T2-fore 중 하나를 선택할 수 있습니다. 동전을 던지세요.

FSM의 각 단계에는 두 가지 선택 (두 개의 스레드-두 가지 선택)이 있으며 코인 토스는 어떤 특정 상태를 따를 수 있는지 결정할 수 있습니다.


좋은 설명 감사합니다. 그리고 멀티 코어 머신은 어떻습니까? Java 상태 머신에서 코어를 악용하는 명시적인 방법이 없다고 생각합니다. 이를 위해서는 OS에 의존해야합니까?
Victor Sorokin

5
멀티 코어 머신은 스케줄링을 약간 더 복잡하게 만듭니다. 그러나 모든 코어는 하나의 공통 메모리에 쓰므로 두 코어 사이의 메모리 쓰기 순서는 본질적으로 인터리브 된 메모리 쓰기 실행으로 돌아갑니다. OS는 코어를 이용하고 JVM은이를 활용합니다. 그것에 대해 두 번 생각할 필요가 없습니다.
S.Lott

9

쓰기 차단 기능은 상태 머신을 만들 수없는 사람들을위한 것입니다.)

스레드는 차단을 피할 수없는 경우에 유용합니다. 기본적인 컴퓨터 활동이 진정으로 차단되는 것은 아니며, 사용하기 쉽도록 많은 방식으로 구현 된 것입니다. 문자 또는 "읽기 실패"를 반환하는 대신 전체 버퍼를 읽을 때까지 읽기 기능이 차단됩니다. 큐에서 리턴 메시지를 확인하고없는 경우 리턴하는 대신 연결 함수가 응답을 기다립니다.

상태 머신 ( "고정"할 수없는 것 이상)에서는 차단 기능을 사용할 수 없습니다.

그리고 예, 상태 머신을 사용하는 것이 실행 가능한 대안입니다. 실시간 시스템에서는 시스템에 대한 프레임 워크를 제공하는 유일한 옵션입니다. 스레드와 차단 기능을 사용하는 것은 "쉬운 방법"입니다. 일반적으로 차단 기능에 대한 한 번의 호출이 상태 머신의 약 3-4 상태를 대체하기 때문입니다.


단일 컨텍스트 실행 프로그램의 코드 페이지 오류는 근본적으로 진정으로 차단됩니다. 정의상 단일 실행 컨텍스트가있는 코드는 플로우의 다음 코드 청크가 사용 가능할 때까지 앞으로 진행할 수 없습니다.
David Schwartz

1
@David Schwartz : 사실 기본적으로 차단됩니다. 그러나 차단 된 코드가하는 것이 아니라 발생하는 것이기 때문에 '작동'이 아닙니다.
Javier

1
파일 읽기는 기본적으로 차단되지 않습니다. 항상 지정된 위치의 읽기 요청을 요청하고 요청이 완료된 후 버퍼에서 데이터를 가져 오는 것으로 분할 할 수 있습니다. 그리고 페이지 오류는 우연 / 휴리스틱 스왑 사용에 대한 해결 방법입니다. 실행에 필요한 모든 데이터가 사용 가능해지기 전에 주어진 상태가 입력되면 발생합니다. 예견이 부족하여 상태 머신 개념에 반대되는 것입니다. 스왑 아웃 및 스왑 인 작업이 상태 시스템의 일부인 경우 페이지 오류가 발생하지 않습니다.
SF.

1
@David Schwartz : "차단"동작의 정의에는 항상 "실시간"요구 사항이 적용됩니다. 예를 들어 코드 페이지 오류는 수백 밀리 초 정도의 응답 성이 필요한 응용 프로그램에 대한 비 차단으로 간주됩니다. 반면에 응용 프로그램에 엄격한 실시간 요구 사항이 있으면 가상 메모리를 전혀 사용하지 않습니다.
MaR

1
@Mar : ... 필요한 데이터를 가져 오기 전에 필요한 데이터를 가져 오는 것을 보장하는 결정적 스왑 알고리즘을 사용하십시오.
SF.

9

하나의 스레드와 상태 시스템 만 사용하여 Java와 같은 고급 언어의 멀티 스레딩 기능을 어떻게 달성합니까? 예를 들어, 수행해야 할 두 가지 활동 (계산 및 I / O 수행)이 있고 하나의 활동이 차단 될 수 있다면 어떨까요?

당신이 묘사하는 것은 협동 멀티 태스킹 ( cooperative multitasking )이라고하며 , 여기서 작업에는 CPU가 주어지고 일정 시간 또는 활동이 결정된 후에 자발적으로 CPU를 포기할 것으로 예상됩니다. CPU를 계속 사용하거나 전체 작업을 차단하고 하드웨어 워치 독 타이머가 부족하여 협력하지 않는 작업은 작업을 감독하는 코드가 없습니다.

최신 시스템에서 볼 수있는 것은 선점 형 멀티 태스킹 ( preemptive multitasking )이라고하며 , 여기서 하드웨어 생성 인터럽트가 도착했을 때 수퍼바이저가 CPU를 대신하기 때문에 작업이 CPU를 포기하지 않아도됩니다. 수퍼바이저의 인터럽트 서비스 루틴은 CPU 상태를 저장하고 다음에 작업이 타임 슬라이스를받을 가치가 있다고 간주 될 때이를 복원 한 다음 다음에 실행될 작업에서 상태를 복원하고 아무 일도 없었던 것처럼 다시 되돌아갑니다. . 이 동작을 컨텍스트 전환 이라고 하며 비용이 많이들 수 있습니다.

고급 언어에서 멀티 스레딩을 대체 할 수있는 "상태 머신 전용"방식을 사용하고 있습니까?

생존 가능한? 확실한. 산네? 때때로. 스레드를 사용하든 어떤 형태의자가 양조 협업 멀티 태스킹 (예 : 상태 머신)을 사용하든간에 원하는 트레이드 오프에 따라 다릅니다.

스레드는 작업 공간을 다른 사람과 데이터 공간을 공유하는 자체 프로그램으로 취급 할 수있는 지점까지 작업 설계를 단순화합니다. 따라서 한 번에 반복 작업을 수행하는 데 필요한 모든 관리 및 하우스 키핑이 아니라 현재 작업에 집중할 수 있습니다. 그러나 선행은 처벌받지 않기 때문에 상황 전환에서 모든 편의를 지불합니다. 최소한의 작업을 수행 한 후 (자발적으로 또는 I / O와 같이 차단되는 작업을 수행하여) CPU를 생성하는 스레드가 많으면 컨텍스트 전환을 수행하는 데 많은 프로세서 시간이 소요될 수 있습니다. 차단 작업이 아주 오랫동안 차단되지 않는 경우 특히 그렇습니다.

협동 경로가 더 의미있는 상황이 있습니다. 폴링이 필요한 메모리 매핑 된 인터페이스를 통해 여러 채널의 데이터를 스트리밍하는 하드웨어 용 사용자 소프트웨어를 한 번 작성해야했습니다. 모든 채널은 스레드로 실행하거나 단일 폴링주기를 반복적으로 실행할 수있는 방식으로 구축 된 객체였습니다.

멀티 스레드 버전의 성능은 위에서 설명한 이유 때문에 전혀 좋지 않았습니다. 각 스레드는 최소한의 작업을 수행 한 다음 CPU를 생성하여 다른 채널이 시간을 갖도록하여 컨텍스트 전환이 많이 발생합니다. 선점 된 처리량이 도움이 될 때까지 스레드를 자유롭게 실행하면 하드웨어에서 시간 초과가 발생하지 않아 버퍼 오버런이 발생하기 전에 일부 채널이 서비스되지 않을 수 있습니다.

각 채널의 반복 작업까지 수행 한 단일 스레드 버전은 열렬한 원숭이처럼 실행되었으며 시스템의로드는 바위처럼 떨어졌습니다. 추가 성능에 대해 지불 한 페널티는 직접 과제를 저글링해야했습니다. 이 경우 코드를 개발하고 유지 관리하는 데 드는 비용이 성능 향상에 가치가있을 정도로 간단했습니다. 나는 이것이 실제로 결론이라고 생각합니다. 내 스레드가 시스템 호출이 돌아 오기를 기다리는 자리에 있었다면 아마도 그만한 가치가 없었을 것입니다.

그것은 Cox의 의견을 알려줍니다. 쓰레드는 상태 머신을 쓸 수없는 사람들만을위한 것이 아닙니다 . 일부 사람들은 그렇게 할 수 있지만 작업을 더 빨리 또는 덜 복잡하게하기 위해 미리 준비된 상태 머신 (예 : 스레드)을 사용하도록 선택합니다.


2

수행해야 할 두 가지 활동 (계산 및 I / O 수행)이 있고 하나의 활동이 차단 될 수 있다면 어떨까요?

글쎄, 스레드없이 I / O 차단을 처리하는 방법을 솔직히 상상할 수 없습니다. 그것을 호출하는 코드가해야만하기 때문에 결국 블로킹 이라고 합니다 wait.

원래 Cox의 전자 메일을 읽은 결과 (아래)는 스레딩이 제대로 확장되지 않지만 지적합니다. 100 개의 I / O 요청이 있으면 어떻게해야합니까? 1000? 10000? Cox는 많은 수의 스레드가 있으면 심각한 문제가 발생할 수 있다고 지적합니다.

보낸 사람 : Alan Cox (alan@lxorguk.ukuu.org.uk)
날짜 : Fri Jan 21 2000-13: 33 :52 EST

응용 프로그램이 많은 수의 스레드에 의존하는 경우 항상 스케줄러와 충돌 할 것입니까? 많은 사람들이 문제에 대해 많은 스레드를 던지며 실제로 나쁜 디자인 일 수 있습니다.

그것은 당신의 걱정 중 가장 적은 것입니다. 1000 개의 스레드는 8Mb의 커널 스택이며 대부분의 캐시를 끌 수 있도록 작업을 충분히 전환합니다. 컴퓨터는 상태 머신입니다. 스레드는 상태 머신을 프로그래밍 할 수없는 사람들을위한 것입니다.

리눅스가 비동기 블록 I / O와 같은 상황에 도움이되지 않는 경우가 많다.

앨런

출처 : Re : IBM의 리눅스 커널 스레딩에 대한 흥미로운 분석 (linux-kernel 메일 링리스트 아카이브)


1
  • 이론적으로 이것은 사실이다. 실제로 스레드는 이러한 상태 머신을 프로그래밍하는 데 사용되는 효율적인 추상화입니다. 그것들은 상태 차트와 페트리 네트 (예를 들어 상태 머신이 기본적으로 순차적 인 병렬 동작)를 프로그래밍하는 데 사용될 수 있도록 매우 효율적입니다.

  • 상태 머신의 문제는 조합 폭발입니다. 4G RAM이있는 컴퓨터의 상태 수는 2 ^ (2 ^ 32) 상태입니다 (2T 디스크 드라이브는 계산하지 않음).

  • 유일한 도구가 망치 인 사람에게는 모든 문제가 못처럼 보입니다.


1

스레드는 두 가지 경우에 유일한 옵션입니다.

  • 메모리 분리없이 여러 코어를 사용합니다.
  • 차단하는 외부 코드에 대처합니다.

두 번째 이유는 대부분의 사람들이 스레드가 IO 또는 네트워크 프로그래밍을 수행하는 데 불가피하다고 생각하는 이유입니다. 그러나 이는 일반적으로 OS에 고급 API가 있는지 알지 못하거나 사용과 싸우고 싶지 않기 때문입니다.

사용하기 편리하고 가독성에 관해서는, (같은 이벤트 루프 항상있다 libev 또는 EventMachine 스레드와 함께 그 일을 거의 간단하게 상태 머신 (state machine)을 프로그래밍, 아직 동기화 문제에 대해 잊을 수있는 충분한 제어 할 수 있도록 도와주는).


1
두 가지 장점 중 하나 : 스레드의 소형 블로킹 상태 머신은 매우 간단한 애플리케이션 코드를 만듭니다. 그리고 스레드가 있으면 코어로 훌륭하게 분할됩니다. 모든 것이 적어도 두 개의 코어를 가지고 있지 않다면, 그것은 곧 될 것입니다. 즉, 쿼드 코어 암 기반 전화기가 2012 년에 출시 될 예정입니다.
Tim Williscroft

0

상태 머신과 멀티 스레딩이 상호 작용하는 방식을 파악하는 좋은 방법은 GUI 이벤트 핸들러를 보는 것입니다. 많은 GUI 응용 프로그램 / 프레임 워크는 가능한 입력 소스를 폴링하고 수신 된 각 입력에 대해 함수를 호출하는 단일 GUI 스레드를 사용합니다. 본질적으로 이것은 거대한 스위치로 작성 될 수 있습니다.

while (true) {
    switch (event) {
        case ButtonPressed:
        ...
        case MachineIsBurning:
        ....
    }
}

이제이 구성에서 높은 수준의 제어 수준이 높을 수 없다는 것이 매우 명확 해졌습니다. ButtonPressed의 처리기는 사용자 상호 작용없이 완료해야하며 기본 루프로 돌아 가야합니다. 그렇지 않으면 더 이상 사용자 이벤트가 발생하지 않기 때문입니다. 처리 될 수 있습니다. 저장할 상태가있는 경우이 상태는 전역 또는 정적 변수에 저장해야하지만 스택에는 저장되지 않아야합니다. 즉, 명령형 언어의 정상적인 제어 흐름이 제한됩니다. 기본적으로 상태 머신으로 제한됩니다.

예를 들어 재귀 수준을 저장해야하는 중첩 서브 루틴이있는 경우 상당히 혼란 스러울 수 있습니다. 또는 파일을 읽는 중이지만 현재 파일을 사용할 수 없습니다. 또는 계산 시간이 길다. 이 모든 경우에, 현재 실행 상태를 저장하고 메인 루프로 돌아가는 것이 바람직하며, 이것이 멀티 스레딩 입니다. 더 이상 아무것도 없습니다.

선점 형 멀티 스레딩 (예 : 스레드가 제어를 수행해야하는시기를 결정하는 운영 체제)이 도입되면서 모든 것이 조금 더 복잡해 졌으므로 오늘날 연결이 즉시 명확하지 않습니다.

따라서 최종 질문에 대답하기 위해 : 예, 상태 머신은 대안이며, 대부분의 GUI는 GUI 스레드와 같은 방식으로 작동합니다. 상태 머신을 너무 멀리 밀지 않으면 실제로 유지 관리 할 수 ​​없게됩니다.


0

상태 머신을 사용하는 것이 고급 언어로 실행 가능한지 묻는 것은 어셈블러로 작성하는 것이 고급 언어를 사용하는 대신 실행 가능한 대안인지를 묻는 것과 조금 다릅니다. 올바른 상황을 감안할 때 둘 다 자리가 있습니다.

스레딩 사용의 추상화는보다 복잡한 병렬 시스템을 구현하기 쉽게 만들어 주지만 궁극적으로 모든 병렬 시스템 에는 동일한 문제가 있습니다. Deadlock / Livelock우선 순위 반전 과 같은 고전적인 문제 는 상태 머신 기반 시스템에서 공유 메모리 병렬 , NUMA 또는 심지어 CSP 기반 시스템 (복잡한 경우)과 마찬가지로 가능합니다.


0

필자는 상태 머신이 매우 '우아한'컴퓨팅 개념이라고 생각하지만 실제로는 매우 복잡합니다. 복잡한 일을 제대로하기가 어렵습니다. 옳지 않은 것은 깨지기 때문에 Alan Cox의 추정되는 키의 천재가 아니라면 당신이 알고있는 것들을 고수하십시오- '영리한 코딩'을 학습 프로젝트에 맡기십시오.

누군가가 헛된 일을 시도했을 때 알 수 있습니다. 유지 관리와 관련하여 (잘 작동한다고 가정), 작업이 불가능한 것임을 알 수 있습니다. 원래 '천재'는 간신히 이해할 수없는 코드를 남길 것입니다 (이러한 유형의 개발자는 기술 문서는 말할 것도없이 너무 많은 주석을 남기지 않습니다).

어떤 경우에는 상태 머신이 더 나은 선택이 될 것입니다-이제 일부 상태 머신 패턴이 사용되고 반복적이고보다 공식적인 방식으로 사용되는 임베디드 유형의 물건을 생각하고 있습니다 (예 : 적절한 엔지니어링 :))

스레딩도 제대로 이해하기 어려울 수 있지만 주로 스레드간에 데이터를 공유 할 필요성을 줄임으로써 도움이되는 패턴이 있습니다.

이것에 대한 마지막 요점은 현대 컴퓨터는 어쨌든 많은 코어에서 실행되므로 상태 머신은 실제로 사용 가능한 리소스를 잘 활용하지 못한다는 것입니다. 스레딩은 여기서 더 나은 작업을 수행 할 수 있습니다.


2
상태 머신은 전혀 복잡하지 않습니다! 복잡한 상태 머신은 복잡하지만 모든 복잡한 시스템도
마찬가지입니다

2
"시도하지 마십시오"는 -1입니다. 그것은 당신이 줄 수있는 최악의 조언입니다.
Javier

1
-1 "시도하지 마십시오"? 그것은 어리석은 일입니다. 또한 상태 머신이 어렵다는 당신의 주장에 도전 할 것입니다. 일단 Heirarchal Fuzzy State Machine과 같은 것에 들어가면 조금 더 복잡해집니다. 그러나 간단한 상태 머신? 그것은 2 학년마다 제가 학교에있을 때 배운 매우 기본적인 것들입니다.
Steven Evers

'dont try'비트를 다시 말해 보도록하겠습니다.
gbjbaanb

0

스레드 대신 적절한 상태 머신 사용법의 좋은 예 : nginx vs apache2. 일반적으로 nginx가 하나의 스레드에서 모든 연결을 처리한다고 가정 할 수 있습니다. apache2는 연결 당 스레드를 만듭니다.

그러나 나에게 상태 머신 대 스레드를 사용하는 것은 완벽하게 수작업으로 만들어진 asm vs java를 사용하는 것과 매우 유사합니다. 당신은 이해할 수없는 결과를 얻을 수 있지만 많은 프로그래머 노력, 많은 훈련, 프로젝트가 더 복잡하고 가치가 있습니다. 다른 많은 프로그래머들. 따라서 빠른 웹 서버를 만들고 싶은 사람이라면 상태 machins 및 비동기 IO를 사용하십시오. 프로젝트를 작성하는 경우 (모든 곳에서 사용할 라이브러리가 아님)-스레드를 사용하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.