코 루틴에 대한 기초는 대부분 60 년대 / 70 년대에 일어 났으며 대안 (예 : 실)을 위해 중단되었습니다.
파이썬과 다른 언어로 발생하고있는 코 루틴에 대한 관심이 새로워 질만한 물질이 있습니까?
코 루틴에 대한 기초는 대부분 60 년대 / 70 년대에 일어 났으며 대안 (예 : 실)을 위해 중단되었습니다.
파이썬과 다른 언어로 발생하고있는 코 루틴에 대한 관심이 새로워 질만한 물질이 있습니까?
답변:
코 루틴은 떠나지 않았으며, 그 동안 다른 것들에 의해 가려졌습니다. 최근 비동기 프로그래밍과 코 루틴에 대한 관심이 증가한 것은 크게 세 가지 요소, 즉 함수형 프로그래밍 기술에 대한 수용 증가, 진정한 병렬 처리 (JavaScript! Python!)에 대한 지원이 부족한 도구 세트 및 가장 중요한 요소 인 스레드와 코 루틴 간의 다른 트레이드 오프 때문입니다. 일부 사용 사례의 경우 코 루틴이 객관적으로 좋습니다.
80 년대, 90 년대, 그리고 오늘날 가장 큰 프로그래밍 패러다임 중 하나는 OOP입니다. OOP의 역사와 특히 Simula 언어의 개발을 살펴보면 클래스가 코 루틴에서 진화 한 것을 볼 수 있습니다. 시뮬라는 개별 이벤트가있는 시스템의 시뮬레이션을 위해 고안되었습니다. 시스템의 각 요소는 하나의 시뮬레이션 단계 동안 이벤트에 응답하여 실행 된 다음 다른 프로세스가 작업을 수행하도록하는 별도의 프로세스였습니다. Simula 67을 개발하는 동안 수업 컨셉이 소개되었습니다. 이제 코 루틴의 지속적 상태가 객체 멤버에 저장되고 이벤트는 메소드를 호출하여 트리거됩니다. 자세한 내용 은 Nygaard & Dahl 의 SIMULA 언어 개발 논문을 참조하십시오 .
그래서 우리는 재미난 방식으로 코 루틴을 계속 사용했습니다. 우리는 그것들을 객체와 이벤트 중심 프로그래밍이라고 부릅니다.
병렬 처리와 관련하여 적절한 메모리 모델이있는 언어와 그렇지 않은 언어의 두 가지 언어가 있습니다. 메모리 모델은“변수에 쓰고 다른 스레드에서 해당 변수를 읽은 후에 이전 값이나 새 값 또는 유효하지 않은 값을 볼 수 있습니까? '전'과 '후'는 무엇을 의미합니까? 어떤 작업이 원 자성으로 보장됩니까?”
좋은 메모리 모델을 만드는 것은 어렵 기 때문에 이러한 노력은 Perl, JavaScript, Python, Ruby, PHP와 같이 지정되지 않은 구현 정의 동적 오픈 소스 언어의 대부분에 대해 수행 된 적이 없습니다. 물론 모든 언어는 원래 작성된 "스크립트"를 훨씬 뛰어 넘었습니다. 이러한 언어 중 일부에는 일종의 메모리 모델 문서가 있지만 충분하지 않습니다. 대신 해킹이 있습니다.
Perl은 스레딩 지원으로 컴파일 할 수 있지만 각 스레드에는 완전한 인터프리터 상태의 개별 복제본이 포함되어있어 스레드가 엄청나게 비쌉니다. 유일한 이점으로서,이 무 공유 접근 방식은 데이터 경쟁을 피하고 프로그래머가 대기열 / 신호 / IPC를 통해서만 통신하도록합니다. Perl은 비동기 처리에 대한 강력한 이야기가 없습니다.
JavaScript는 항상 함수형 프로그래밍을 풍부하게 지원하므로 프로그래머는 비동기 작업이 필요한 프로그램에서 연속체 / 콜백을 수동으로 인코딩합니다. 예를 들어 Ajax 요청 또는 애니메이션 지연이 있습니다. 웹은 본질적으로 비동기이기 때문에 많은 비동기 JavaScript 코드가 있으며 이러한 모든 콜백을 관리하는 것은 매우 고통 스럽습니다. 따라서 이러한 콜백을 더 잘 조직하거나 (약속) 완전히 제거하려는 많은 노력이 있습니다.
파이썬에는 Global Interpreter Lock이라는 불행한 기능이 있습니다. 기본적으로 파이썬 메모리 모델은“병렬화가 없기 때문에 모든 효과가 순차적으로 나타납니다. 한 번에 하나의 스레드 만 Python 코드를 실행합니다.”따라서 Python에는 스레드가 있지만 코 루틴만큼 강력합니다. [1] 파이썬은로 제너레이터 함수를 통해 많은 코 루틴을 인코딩 할 수 있습니다 yield
. 올바르게 사용하면 JavaScript로 알려진 대부분의 콜백 지옥을 피할 수 있습니다. Python 3.5의 최신 비동기 / 대기 시스템은 Python에서 비동기 관용구를 더 편리하게 만들고 이벤트 루프를 통합합니다.
[1] : 기술적으로 이러한 제한은 Python 참조 구현 인 CPython에만 적용됩니다. 자이 썬 (Jython)과 같은 다른 구현은 병렬로 실행할 수있는 실제 스레드를 제공하지만 동등한 동작을 구현하려면 많은 시간이 소요된다. 기본적으로 모든 변수 또는 객체 멤버는 휘발성 변수이므로 모든 변경 사항이 원자 적이며 모든 스레드에서 즉시 볼 수 있습니다. 물론 휘발성 변수를 사용하는 것이 일반 변수를 사용하는 것보다 훨씬 비쌉니다.
나는 루비와 PHP에 대해 제대로 구울만큼 충분하지 않습니다.
요약하자면, 이러한 언어 중 일부는 멀티 스레딩을 바람직하지 않거나 불가능하게 만드는 기본 설계 결정을 가지고있어 코 루틴과 같은 대안과 비동기 프로그래밍을보다 편리하게 만드는 방법에 더 중점을 둡니다.
마지막으로 코 루틴과 스레드의 차이점에 대해 이야기하겠습니다.
프로세스 내부의 여러 스레드가 메모리 공간을 공유한다는 점을 제외하면 스레드는 기본적으로 프로세스와 같습니다. 이는 스레드가 메모리 측면에서 결코 "경량"이 아님을 의미합니다. 스레드는 운영 체제에 의해 사전 예약됩니다. 이는 작업 스위치의 오버 헤드가 높고 불편한 시간에 발생할 수 있음을 의미합니다. 이 오버 헤드에는 스레드 상태를 일시 중단하는 비용과 사용자 모드 (스레드의 경우)와 커널 모드 (스케줄러의 경우) 사이의 전환 비용이 있습니다.
프로세스가 자체 스레드를 직접 협력 적으로 예약하는 경우 컨텍스트를 커널 모드로 전환 할 필요가 없으며 간접 작업 호출에 비해 작업을 전환하는 비용이 상당히 저렴합니다. 이러한 경량 실은 다양한 세부 사항에 따라 녹색 실, 섬유 또는 코 루틴이라고 할 수 있습니다. 녹색 스레드 / 섬유의 주목할만한 사용자는 초기 Java 구현이었으며, 최근에는 Golang의 Goroutines였습니다. 코 루틴의 개념적 장점은 코 루틴 사이에서 명시 적으로 앞뒤로 전달되는 제어 흐름의 측면에서 실행을 이해할 수 있다는 것입니다. 그러나 이러한 코 루틴은 여러 OS 스레드에서 예약되지 않으면 진정한 병렬 처리를 달성 할 수 없습니다.
싼 코 루틴은 어디에 유용합니까? 대부분의 소프트웨어에는 gazillion 스레드가 필요하지 않으므로 일반적으로 비싼 스레드는 정상입니다. 그러나 비동기 프로그래밍은 때때로 코드를 단순화 할 수 있습니다. 자유롭게 사용 되려면이 추상화가 충분히 저렴해야합니다.
그리고 웹이 있습니다. 위에서 언급했듯이 웹은 본질적으로 비동기 적입니다. 네트워크 요청은 단순히 오랜 시간이 걸립니다. 많은 웹 서버가 작업자 스레드로 가득 찬 스레드 풀을 유지 관리합니다. 그러나 디스크에서 파일을로드 할 때 I / O 이벤트를 대기하거나 클라이언트가 응답의 일부를 승인 할 때까지 또는 데이터베이스까지 대기하기 때문에 일부 스레드를 대기하기 때문에 이러한 스레드는 대부분 유휴 상태입니다. 쿼리가 완료됩니다. NodeJS는 결과적으로 이벤트 기반 및 비동기 서버 디자인이 매우 잘 작동한다는 것을 놀랍게 입증했습니다. 분명히 JavaScript는 웹 응용 프로그램에 사용되는 유일한 언어와는 거리가 멀기 때문에 비동기 웹 프로그래밍을보다 쉽게하기 위해 다른 언어 (Python 및 C #에서 주목할만한)에 대한 큰 인센티브도 있습니다.
코 루틴은 운영 체제가 선제 적 스케줄링을 수행하지 않았기 때문에 유용했습니다 . 그들이 선제 적 스케줄링을 제공하기 시작하면, 프로그램에서 주기적으로 제어권을 포기해야했습니다.
멀티 코어 프로세서가 널리 보급됨에 따라 코 루틴은 작업 병렬 처리 를 달성 하고 시스템 활용도를 높이기 위해 사용됩니다 (한 실행 스레드가 리소스를 기다려야하는 경우 다른 스레드가 대신 실행될 수 있음).
NodeJS는 코 루틴을 사용하여 IO에 병렬로 액세스하는 특수한 경우입니다. 즉, 여러 스레드가 IO 요청을 처리하는 데 사용되지만 단일 스레드가 자바 스크립트 코드를 실행하는 데 사용됩니다. signle 스레드에서 사용자 코드를 실행하는 목적은 뮤텍스를 사용할 필요가 없도록하는 것입니다. 이것은 위에서 언급 한 것처럼 시스템 활용률을 높이려고하는 범주에 속합니다.
초기 시스템은 동시성을 제공하기 위해 코 루틴을 사용했습니다. 스레드는 운영 체제로부터 상당한 양의 지원을 필요로하며 (사용자 레벨에서 구현할 수 있지만 시스템이 주기적으로 프로세스를 중단하도록 배열 할 수있는 방법이 필요함) 지원이있는 경우에도 구현하기가 어렵습니다. .
스레드는 70 ~ 80 년대에 모든 심각한 운영 체제가 (그리고 90 년대, 심지어 Windows까지) 지원했으며 더 일반적이기 때문에 나중에 인수를 시작했습니다. 그리고 사용하기가 더 쉽습니다. 갑자기 모든 사람들이 스레드가 다음 큰 일이라고 생각했습니다.
90 년대 후반에 균열이 나타나기 시작했으며 2000 년대 초에는 실에 심각한 문제가 있음이 분명해졌습니다.
시간이 지남에 따라 프로그램이 일반적으로 언제라도 수행해야하는 작업 수가 빠르게 증가하여 위의 (1) 및 (2)로 인해 발생하는 문제가 증가했습니다. 프로세서 속도와 메모리 액세스 시간의 불일치가 증가하여 문제가 악화되었습니다 (3). 그리고 필요한 자원의 수와 종류에 따라 프로그램의 복잡성이 증가하면서 문제의 관련성이 높아졌습니다 (4).
그러나 약간의 일반성을 잃고 프로그래머가 프로세스를 어떻게 작동시킬 수 있는지에 대해 약간의 생각을함으로써 코 루틴은 이러한 모든 문제를 해결할 수 있습니다.
나는 코 루틴 이 부활, 병렬 처리를 얻지 못하는 이유를 진술하면서 시작하고 싶습니다 . 일반적으로 현대 코 루틴은 작업 기반 병렬 처리를 수행하는 수단 이 아닙니다 . 현대 구현에서는 다중 처리 기능을 사용하지 않기 때문입니다. 가장 가까운 것은 섬유 와 같은 것 입니다.
현대 코 루틴은 haskell과 같은 기능적 언어에서 매우 유용한 게으른 평가 를 달성하는 방법으로 왔으며, 전체 세트를 반복하여 작업을 수행하는 대신 필요한만큼만 작업을 수행 할 수 있습니다. 무한한 아이템 세트 또는 조기 종료 및 서브 세트가있는 큰 세트에 유용합니다.
Python 및 C #과 같은 언어로 생성기 (자체 평가 요구의 일부를 충족시키는) 를 생성하기 위해 Yield 키워드를 사용 함으로써 현대 구현에서 코 루틴은 가능했을뿐만 아니라 언어 자체의 특별한 구문 없이도 가능했습니다. (파이썬은 결국 몇 가지 비트를 추가하여 도움을주었습니다). 의 아이디어와 게으른 evaulation와 공동 루틴 도움이 미래 의 당신이 그 시간에 변수의 값을 필요로하지 않는 경우에, 당신은 당신이 명시 적으로 그 값을 요청할 때까지 획득 실제로 지연시킬 수 있습니다 (이 값을 사용할 수 있도록하고 인스턴스화와 다른 시간에 느리게 평가하십시오 .)
그러나 특히 웹 스피어에서 게으른 평가 외에도 이러한 공동 루틴은 콜백 지옥을 해결하는 데 도움이 됩니다. 코 루틴은 데이터베이스 액세스, 온라인 트랜잭션, UI 등에 유용합니다. 클라이언트 시스템 자체의 처리 시간으로 인해 필요한 것에 더 빠르게 액세스 할 수 없습니다. 스레딩은 같은 것을 완전히 채울 수 있지만이 영역에서 더 많은 오버 헤드가 필요하며 코 루틴과 달리 실제로 작업 병렬 처리에 유용합니다 .
요컨대, 웹 개발이 성장하고 기능적 패러다임이 명령형 언어와 더 많이 통합됨에 따라 코 루틴은 비동기 문제 및 지연 평가에 대한 솔루션으로 등장했습니다. 코 루틴은 일반적으로 다중 프로세스 스레딩 및 스레딩이 불필요하거나 불편하거나 불가능한 문제가있는 공간에옵니다.
Javascript, Lua, C # 및 Python과 같은 언어의 코 루틴은 모두 주 함수를 다른 함수에 대한 제어를 포기 하는 개별 함수 (운영 체제 호출과 무관)로 구현을 파생시킵니다 .
에서 이 파이썬 예를 들어 , 우리는 뭔가라는 재미 있은 파이썬 기능이 await
그 안에 있습니다. 이것은 기본적으로 yield이며, loop
다른 함수 (이 경우 다른 factorial
함수) 를 실행할 수있는 실행을 생성 합니다. "병렬로 실행되는 작업"이라는 말이 잘못된 경우 실제로는 병렬로 실행되지 않고 await 키워드를 사용 하여 인터리빙 기능이 실행 된다는 점 에 유의하십시오 (이는 특별한 유형의 산출량입니다).
그들은 허용되지 않은 단일 병렬에 대한 제어의 수익률 동시 아닌 프로세스 작업 병렬 이러한 작업이 작동하지 않는다는 의미에서, 지금까지 같은 시간에. 코 루틴은 현대 언어 구현의 스레드 가 아닙니다 . 이러한 모든 언어 루틴 공동 구현은 이러한 함수 수율 호출 (프로그래머가 실제로 수동으로 공동 루틴에 입력해야 함)에서 파생됩니다.
편집 : C ++ Boost coroutine2는 같은 방식으로 작동하며 설명은 내가 yeilds와 이야기하고있는 것에 대한 더 나은 시각을 제공해야 합니다 . 여기를 참조하십시오 . 보시다시피 구현에는 "특별한 경우"가 없으며 부스트 섬유 와 같은 것은 예외이며 명시적인 동기화가 필요합니다.
EDIT2 : 누군가가 C # 작업 기반 시스템에 대해 이야기하고 있다고 생각했기 때문에 그렇지 않았습니다. Unity의 시스템 과 순진한 C # 구현에 대해 이야기했습니다.