스레드 풀은 언제 사용됩니까?


104

그래서 Node.js의 작동 방식을 이해했습니다. 이벤트를 수신 한 다음이를 작업자 풀에 위임하는 단일 리스너 스레드가 있습니다. 작업자 스레드는 작업이 완료되면 리스너에게 알리고 리스너는 호출자에게 응답을 반환합니다.

내 질문은 이것이다 : Node.js에서 HTTP 서버를 세우고 라우팅 된 경로 이벤트 중 하나 (예 : "/ test / sleep")에서 sleep을 호출하면 전체 시스템이 중단됩니다. 단일 리스너 스레드조차. 그러나 내 이해는이 코드가 작업자 풀에서 발생한다는 것입니다.

대조적으로 Mongoose를 사용하여 MongoDB와 통신 할 때 DB 읽기는 값 비싼 I / O 작업입니다. Node는 작업을 스레드에 위임하고 완료되면 콜백을받을 수있는 것 같습니다. DB에서로드하는 데 걸리는 시간이 시스템을 차단하지 않는 것 같습니다.

Node.js는 스레드 풀 스레드와 리스너 스레드를 어떻게 사용하기로 결정합니까? 휴면 상태이고 스레드 풀 스레드 만 차단하는 이벤트 코드를 작성할 수없는 이유는 무엇입니까?


@Tobi-나는 그것을 보았다. 여전히 내 질문에 대답하지 않습니다. 작업이 다른 스레드에있는 경우 수면은 해당 스레드에만 영향을 미치며 리스너에게는 영향을주지 않습니다.
Haney

8
스스로 무언가를 이해하려고 노력하고 미로의 출구를 찾을 수 없을 때 도움을 요청하는 진정한 질문입니다.
Rafael Eyng 2015 년

답변:


240

노드의 작동 방식에 대한 이해는 정확하지 않습니다.하지만 상황의 현실은 실제로 상당히 복잡하고 일반적으로 "노드는 단일 스레드"와 같이 일을 지나치게 단순화하는 간결한 작은 문구로 요약되기 때문에 일반적인 오해입니다. .

지금은 clusterwebworker-threads를 통한 명시 적 다중 처리 / 다중 스레드를 무시 하고 일반적인 비 스레드 노드에 대해서만 이야기하겠습니다.

노드는 단일 이벤트 루프에서 실행됩니다. 단일 스레드이며 해당 스레드 하나만 얻을 수 있습니다. 작성한 모든 자바 스크립트는이 루프에서 실행되며, 해당 코드에서 차단 작업이 발생하면 전체 루프를 차단하고 완료 될 때까지 다른 작업은 발생하지 않습니다. 이것은 일반적으로 많이 듣게되는 노드의 단일 스레드 특성입니다. 그러나 전체 그림이 아닙니다.

일반적으로 C / C ++로 작성된 특정 함수 및 모듈은 비동기 I / O를 지원합니다. 이러한 함수와 메서드를 호출하면 내부적으로 작업자 스레드에 대한 호출 전달을 관리합니다. 예를 들어, fs모듈을 사용하여 파일을 요청하면 fs모듈은 해당 호출을 작업자 스레드에 전달하고 해당 작업자는 응답을 기다린 다음 응답을 기다립니다. 그런 다음 그 동안에. 이 모든 것은 노드 개발자 인 당신에게서 추상화되고 일부는 libuv 사용을 통해 모듈 개발자로부터 추상화됩니다 .

Denis Dollfus가 의견에서 지적했듯이 ( 비슷한 질문에 대한 이 답변 에서) libuv가 비동기 I / O를 달성하기 위해 사용하는 전략은 항상 스레드 풀이 아닙니다. 특히 http모듈의 경우 다른 전략이 이때 사용됩니다. 여기서 우리의 목적을 위해 비동기 컨텍스트가 어떻게 달성되는지 (libuv를 사용하여) 그리고 libuv가 유지 관리하는 스레드 풀이 비동기 성을 달성하기 위해 해당 라이브러리에서 제공하는 여러 전략 중 하나라는 점을 주목하는 것이 주로 중요합니다.


주로 관련된 탄젠트에 대해서는 이 훌륭한 기사에서 노드가 어떻게 비동기 성을 달성하는지에 대한 훨씬 더 깊은 분석과 관련된 잠재적 인 문제와이를 처리하는 방법 있습니다. 대부분은 위에서 작성한 내용을 확장하지만 추가로 지적합니다.

  • 네이티브 C ++ 및 libuv를 사용하는 프로젝트에 포함하는 모든 외부 모듈은 스레드 풀을 사용할 가능성이 높습니다 (예 : 데이터베이스 액세스).
  • libuv의 기본 스레드 풀 크기는 4이며 대기열을 사용하여 스레드 풀에 대한 액세스를 관리합니다. 결론은 5 개의 장기 실행 DB 쿼리가 모두 동시에 진행되는 경우 그중 하나 (및 다른 비동기식 스레드 풀에 의존하는 작업)은 해당 쿼리가 시작되기 전에 완료되기를 기다립니다.
  • UV_THREADPOOL_SIZE스레드 풀이 필요하고 생성되기 전에 수행하는 한 환경 변수를 통해 스레드 풀의 크기를 늘려이 문제를 완화 할 수 있습니다 .process.env.UV_THREADPOOL_SIZE = 10;

노드에서 기존의 다중 처리 또는 다중 스레딩을 원하는 경우 내장 cluster모듈 또는 앞서 언급 한 다양한 기타 모듈을 통해 가져 webworker-threads오거나 작업을 덩어리로 만들고 수동으로 setTimeout또는 setImmediate또는 process.nextTick작업을 일시 중지하고 이후 루프에서 계속하여 다른 프로세스를 완료 할 수 있습니다 (권장되지 않음).

자바 스크립트로 장기 실행 / 차단 코드를 작성하는 경우 실수를 한 것입니다. 다른 언어는 훨씬 더 효율적으로 수행됩니다.


1
이런 젠장, 이것은 나를 위해 그것을 완전히 정리합니다. @Jason 정말 감사합니다!
Haney

5
문제 없습니다 :) 얼마 전에 당신이있는 곳에서 저를 찾았습니다. 한쪽에는 대답이 분명한 C / C ++ 개발자가 있고 다른 한쪽에는 전형적인 대답이 있기 때문에 잘 정의 된 대답을 찾기가 어려웠습니다. 이전에 이러한 종류의 질문에 너무 깊이 들어 가지 않은 웹 개발자. C 레벨까지 내려 갔을 때 제 대답이 기술적으로 100 % 정확한지 확신 할 수는 없지만 광범위한 스트로크에서는 맞습니다.
Jason

3
네트워크 요청에 스레드 풀을 사용하는 것은 막대한 리소스 낭비입니다. 이 질문 에 따르면 "스레드 풀없이 epoll, kqueue 및 IOCP와 같은 다른 플랫폼에서 비동기 I / O 인터페이스를 기반으로 비동기 네트워크 I / O를 수행합니다."
Denis Dollfus 2014

1
... 즉, 메인 자바 스크립트 스레드에서 직접 무거운 작업을 수행하거나 리소스가 충분하지 않거나 스레드 풀에 충분한 헤드 룸을 제공하기 위해 적절하게 관리하지 않으면 더 낮은 동시성에서 지연이 발생할 수 있습니다. 임계 값-결론은 동일한 시스템 리소스에 대해 일반적으로 다른 옵션보다 node.js에서 더 높은 처리량을 경험할 수 있다는 것입니다 (이에 도전하는 것을 목표로하는 다른 언어로 된 다른 이벤트 기반 시스템이 있긴하지만) 하지만 최근 벤치 마크 참조)-이벤트 기반 모델이 스레드 모델을 능가한다는 것은 분명합니다.
Jason

1
@Aabid 리스너 스레드는 데이터베이스 쿼리를 실행하지 않으므로 해당 쿼리 10 개를 모두 완료하는 데 약 6 초가 걸립니다 (기본 스레드 풀 크기 4). 데이터베이스 쿼리의 결과를 완료 할 필요가없는 자바 스크립트에서 작업을 수행해야하는 경우, 예를 들어 스레드 풀에서 비동기 작업을 완료 할 필요가없는 더 많은 요청이 들어 오면 메인에서 계속 작동합니다. 이벤트 루프.
Jason

20

그래서 Node.js의 작동 방식을 이해했습니다. 이벤트를 수신 한 다음이를 작업자 풀에 위임하는 단일 리스너 스레드가 있습니다. 작업자 스레드는 작업이 완료되면 리스너에게 알리고 리스너는 호출자에게 응답을 반환합니다.

이것은 실제로 정확하지 않습니다. Node.js에는 자바 스크립트 실행을 수행하는 단일 "작업자"스레드 만 있습니다. 노드 내에 IO 처리를 처리하는 스레드가 있지만이를 "작업자"로 생각하는 것은 오해입니다. 실제로 IO 처리와 노드의 내부 구현에 대한 몇 가지 기타 세부 사항이 있지만 프로그래머로서 MAX_LISTENERS와 같은 몇 가지 기타 매개 변수 외에는 동작에 영향을 줄 수 없습니다.

내 질문은 이것이다 : Node.js에서 HTTP 서버를 세우고 라우팅 된 경로 이벤트 중 하나 (예 : "/ test / sleep")에서 sleep을 호출하면 전체 시스템이 중단됩니다. 단일 리스너 스레드조차. 그러나 내 이해는이 코드가 작업자 풀에서 발생한다는 것입니다.

JavaScript에는 수면 메커니즘이 없습니다. "수면"이 의미한다고 생각하는 코드 스 니펫을 게시하면 더 구체적으로 논의 할 수 있습니다. time.sleep(30)예를 들어, 파이썬에서 와 같은 것을 시뮬레이션하기 위해 호출 할 함수는 없습니다 . 있다 setTimeout그러나 그것은 근본적으로 잠을 잘 수 있습니다. setTimeoutsetInterval명시 적으로 해제 아니라 블록의 코드 비트들은 다른 주 실행 스레드에서 실행할 수 있도록 이벤트 루프. 당신이 할 수있는 유일한 일은 인 메모리 계산으로 CPU를 바쁘게 루프하는 것입니다. 이것은 실제로 메인 실행 스레드를 고갈시키고 프로그램을 응답하지 않게 만듭니다.

Node.js는 스레드 풀 스레드와 리스너 스레드를 어떻게 사용하기로 결정합니까? 휴면 상태이고 스레드 풀 스레드 만 차단하는 이벤트 코드를 작성할 수없는 이유는 무엇입니까?

네트워크 IO는 항상 비동기입니다. 이야기의 끝. 디스크 IO에는 동기 및 비동기 API가 모두 있으므로 "결정"이 없습니다. node.js는 sync와 일반 비동기를 호출하는 API 핵심 함수에 따라 작동합니다. 예를 들면 : fs.readFilefs.readFileSync. 하위 프로세스의 경우 별도 child_process.execchild_process.execSyncAPI도 있습니다.

경험상 항상 비동기 API를 사용합니다. 동기화 API를 사용하는 유효한 이유는 연결을 수신하기 전에 네트워크 서비스의 초기화 코드 또는 빌드 도구 및 그런 종류의 네트워크 요청을 수락하지 않는 간단한 스크립트 때문입니다.


1
이러한 비동기 API의 출처는 어디입니까? 나는 당신이 말하는 것을 얻었지만이 API를 작성한 사람은 IOCP / async를 선택했습니다. 그들은 이것을 어떻게 선택 했습니까?
Haney

3
그의 질문은 그가 블록이 아닌 자신의 시간 집약적 인 코드를 작성하는 방법입니다.
Jason

1
예. 노드는 기본 UDP, TCP 및 HTTP 네트워킹을 제공합니다. 비동기 "풀 기반"API 만 제공합니다. 예외없이 전 세계의 모든 node.js 코드는 사용 가능한 모든 것이 있기 때문에 이러한 풀 기반 비동기 API를 사용합니다. 파일 시스템과 하위 프로세스는 다른 이야기이지만 네트워킹은 일관되게 비동기 적입니다.
Peter Lyons

4
조심해, 피터, 당신이 그의 주전자의 속담이되지 않도록. 그는 네트워크 API를 사용하는 사람들이 어떻게했는지가 아니라 네트워크 API 작성자가 어떻게했는지 알고 싶어합니다. 나는 결국 노드가 어떻게 다시 작동하는지 이해하게되었다. 왜냐하면 네트워킹이나 다른 내장 된 비동기 API와 관련이없는 내 자신의 비 차단 코드를 작성하고 싶었 기 때문이다. 데이비드가 똑같이하고 싶어한다는 것은 분명합니다.
Jason

2
노드는 IO에 대한 스레드 풀을 사용하지 않는, 그것은 사용하는 네이티브 비 차단 IO를 유일한 예외는 fs내가 아는까지로,
vkurchatkin

2

스레드 풀 사용시기 및 사용자 :

먼저 컴퓨터에 Node를 사용 / 설치하면 컴퓨터의 노드 프로세스라고하는 다른 프로세스 중에서 프로세스를 시작하고 사용자가 죽일 때까지 계속 실행됩니다. 이 실행 프로세스는 소위 단일 스레드입니다.

여기에 이미지 설명 입력

따라서 단일 스레드의 메커니즘으로 인해 노드 애플리케이션을 쉽게 차단할 수 있지만 이것은 Node.js가 테이블에 제공하는 고유 한 기능 중 하나입니다. 따라서 노드 애플리케이션을 다시 실행하면 단일 스레드에서만 실행됩니다. 동시에 애플리케이션에 액세스하는 사용자가 1 백만 명이든 백만 명이든 상관 없습니다.

이제 노드 애플리케이션을 시작할 때 nodejs의 단일 스레드에서 일어나는 일을 정확히 이해합시다. 처음에 프로그램이 초기화 된 다음 모든 최상위 코드가 실행됩니다. 즉, 콜백 함수 내에없는 모든 코드가 실행됩니다 (모든 콜백 함수 내의 모든 코드가 이벤트 루프에서 실행된다는 것을 기억하십시오 ).

그 후 실행 된 모든 모듈 코드가 모든 콜백을 등록하고 마지막으로 애플리케이션에 대한 이벤트 루프가 시작됩니다.

여기에 이미지 설명 입력

따라서 모든 콜백 함수와 해당 함수 내의 코드가 이벤트 루프에서 실행되기 전에 논의했듯이. 이벤트 루프에서 부하는 여러 단계로 분산됩니다. 어쨌든 여기서 이벤트 루프에 대해 논의하지 않을 것입니다.

스레드 풀을 더 잘 이해하기 위해 이벤트 루프에서 하나의 콜백 함수 내부의 코드가 다른 콜백 함수 내부의 코드 실행을 완료 한 후 실행되는 것을 상상해 보라고 요청합니다. 이제 일부 작업이 실제로 너무 무겁다면. 그런 다음 nodejs 단일 스레드를 차단합니다. 그래서 이벤트 루프와 같은 스레드 풀이 libuv 라이브러리에 의해 Node.js에 제공되는 곳입니다.

따라서 스레드 풀은 nodejs 자체의 일부가 아니며 libuv에 의해 제공되어 무거운 업무를 libuv에 오프로드하고 libuv는 해당 코드를 자체 스레드에서 실행하고 실행 후 libuv는 이벤트 루프의 이벤트에 결과를 반환합니다.

여기에 이미지 설명 입력

스레드 풀은 기본 단일 스레드와 완전히 별 개인 4 개의 추가 스레드를 제공합니다. 그리고 실제로 최대 128 개의 스레드를 구성 할 수 있습니다.

따라서이 모든 스레드가 함께 스레드 풀을 형성했습니다. 그러면 이벤트 루프가 자동으로 무거운 작업을 스레드 풀로 오프로드 할 수 있습니다.

재미있는 부분은이 모든 것이 장면 뒤에서 자동으로 발생한다는 것입니다. 스레드 풀로 이동하는 것과 그렇지 않은 것을 결정하는 것은 우리 개발자가 아닙니다.

스레드 풀에는 다음과 같은 많은 작업이 있습니다.

-> All operations dealing with files
->Everyting is related to cryptography, like caching passwords.
->All compression stuff
->DNS lookups

0

이 오해는 선제 적 멀티 태스킹과 협동 적 멀티 태스킹의 차이 일뿐입니다.

모든 놀이기구에 실제로 한 줄이 있기 때문에 수면은 카니발 전체를 끄고 게이트를 닫았습니다. "JS 인터프리터와 다른 것들"이라고 생각하고 스레드를 무시하십시오 ... 당신에게는 스레드가 하나뿐입니다.

... 차단하지 마십시오.

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