[asp.net] 트래픽이 많은 시나리오에서 ASP.NET에서 ThreadPool.QueueUserWorkItem 사용

저는 항상 ThreadPool을 사용하여 (중요하지 않은) 단기 백그라운드 작업이 ASP.NET에서도 모범 사례로 간주된다는 인상을 받았습니다. 그러나 다른 방법을 제안하는 것처럼 보이는 이 기사 를 보았습니다. ASP.NET 관련 요청을 처리하려면 ThreadPool을 떠나야합니다.

지금까지 작은 비동기 작업을 수행 한 방법은 다음과 같습니다.

ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))

그리고 기사는 명시 적으로 유사한 스레드를 생성하는 대신에 제안한다 :

new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()

첫 번째 방법은 관리되고 제한된다는 이점이 있지만, 백그라운드 작업이 ASP.NET 요청 처리기를 사용하여 스레드를두고 경쟁 할 가능성이 있습니다 (문서가 올바른 경우). 두 번째 방법은 ThreadPool을 해제하지만 제한이 없어서 잠재적으로 너무 많은 리소스를 사용할 수 있습니다.

그래서 제 질문은 기사의 조언이 맞습니까?

사이트에 트래픽이 너무 많아서 ThreadPool이 가득 차면 대역 외로 이동하는 것이 더 낫거나 전체 ThreadPool이 어쨌든 리소스의 한계에 도달하고 있음을 의미합니다. 자신의 스레드를 시작하려고하지 않아야합니까?

설명 : 별도의 프로세스가 필요한 값 비싼 작업 항목이 아닌 소규모의 중요하지 않은 비동기 작업 (예 : 원격 로깅)의 범위에서 요청하는 것입니다 (이 경우 더 강력한 솔루션이 필요하다는 데 동의합니다).



답변

여기에 다른 답변은 가장 중요한 점을 생략 한 것 같습니다.

부하가 적은 사이트에서 더 빠르게 수행하기 위해 CPU 집약적 인 작업을 병렬화하려고하지 않는 한 작업자 스레드를 사용할 필요가 없습니다.

에 의해 생성 된 사용 가능한 스레드 new Thread(...)와 요청에 ThreadPool응답하는 의 작업자 스레드 모두에 적용 QueueUserWorkItem됩니다.

예, 사실 입니다. 너무 많은 작업 항목을 대기열에 추가하여 ASP.NET 프로세스에서 굶주릴 ThreadPool 있습니다. ASP.NET이 추가 요청을 처리하지 못하게합니다. 기사의 정보는 그 점에서 정확합니다. QueueUserWorkItem요청을 처리하는 데에도 사용 되는 동일한 스레드 풀이 사용됩니다 .

그러나 실제로 이러한 기아를 유발할 수있는 충분한 작업 항목을 큐에 넣는 경우 스레드 풀이 부족 해야 합니다! 말 그대로 수백 개의 CPU 집약적 작업을 동시에 실행하는 경우 컴퓨터가 이미 과부하 상태 일 때 ASP.NET 요청을 처리 할 다른 작업자 스레드가 있으면 어떤 이점이 있습니까? 이런 상황에 처해 있다면 완전히 재 설계해야합니다!

대부분의 경우 멀티 스레드 코드가 ASP.NET에서 부적절하게 사용되는 것을 보거나 듣습니다. CPU 집약적 인 작업을 대기열에 넣는 것이 아닙니다. I / O 바운드 작업을 큐잉하기위한 것입니다. 그리고 당신은 내가에게 / O 작업을 수행하려는 경우, 당신은 I / O 스레드 (I / O 완료 포트)를 이용해야한다.

특히, 사용중인 라이브러리 클래스에서 지원하는 비동기 콜백을 사용해야합니다. 이러한 방법은 항상 매우 명확하게 표시되어 있습니다. 단어 BeginEnd. 마찬가지로 Stream.BeginRead, Socket.BeginConnect, WebRequest.BeginGetResponse, 등.

이러한 방법은 를 사용 ThreadPool하지만, 그들은 않습니다 IOCPs 사용 하지 ASP.NET 요청을 방해합니다. I / O 시스템의 인터럽트 신호에 의해 “깨어날”수있는 특수한 종류의 경량 스레드입니다. 그리고 ASP.NET 응용 프로그램에서는 일반적으로 각 작업자 스레드에 대해 하나의 I / O 스레드가 있으므로 모든 단일 요청에 하나의 비동기 작업이 대기 될 수 있습니다. 이는 상당한 성능 저하없이 말 그대로 수백 개의 비동기 작업입니다 (I / O 하위 시스템이이를 따라갈 수 있다고 가정). 여러분이 필요로하는 것보다 훨씬 더 많습니다.

비동기 델리게이트 는 이런 방식으로 작동하지 않는다는 점을 명심하십시오. .NET 과 마찬가지로 작업자 스레드를 사용하게 ThreadPool.QueueUserWorkItem됩니다. 이 작업을 수행 할 수있는 것은 .NET Framework 라이브러리 클래스의 기본 제공 비동기 메서드뿐입니다. 직접 할 수 있지만 복잡하고 약간 위험하며 아마도이 논의의 범위를 벗어납니다.

이 질문에 대한 가장 좋은 대답 은 ASP.NET에서 또는 백그라운드 인스턴스를 사용하지 않는 것ThreadPool Thread 입니다. Windows Forms 응용 프로그램에서 스레드를 회전시키는 것과는 전혀 다릅니다. UI의 응답 성을 유지하고 얼마나 효율적인지 신경 쓰지 않습니다. ASP.NET에서 관심사는 처리량 이며 모든 작업자 스레드에서 전환되는 모든 컨텍스트는 사용 여부에 관계없이 처리량 을 절대적으로 죽일ThreadPool입니다.

ASP.NET에서 스레딩 코드를 작성하는 경우-기존 비동기 메서드를 사용하도록 다시 작성할 수 있는지 여부를 고려하고 그렇지 않은 경우 실제로 코드가 필요한지 여부를 고려하십시오. 백그라운드 스레드에서 실행됩니다. 대부분의 경우 순이익이없이 복잡성을 추가 할 수 있습니다.


답변

Microsoft ASP.NET 팀의 Thomas Marquadt에 따르면 ASP.NET ThreadPool (QueueUserWorkItem)을 사용하는 것이 안전합니다.

기사에서 :

Q) 내 ASP.NET 응용 프로그램이 CLR ThreadPool 스레드를 사용하는 경우 요청을 실행하는 데 CLR ThreadPool을 사용하는 ASP.NET도 사용하지 않습니까?
..

A) 요약하자면 ASP.NET의 스레드가 부족 할까 걱정하지 마세요. 여기에 문제가 있다고 생각되면 알려 주시면 처리해 드리겠습니다.

Q) 나만의 스레드 (새 스레드)를 만들어야합니까? CLR ThreadPool을 사용하기 때문에 ASP.NET에 더 적합하지 않을 것입니다.

A)하지 마십시오. 아니면 다른 방식으로 말하면 안돼 !!! 당신이 정말 똑똑하다면-나보다 훨씬 똑똑하다면-당신 자신의 스레드를 만들 수 있습니다. 그렇지 않으면 그것에 대해 생각하지 마십시오. 새 스레드를 자주 생성하지 않아야하는 몇 가지 이유는 다음과 같습니다.

  1. QueueUserWorkItem에 비해 매우 비쌉니다. 그런데 CLR보다 더 나은 ThreadPool을 작성할 수 있다면 Microsoft에 지원하는 것이 좋습니다.

답변

웹 사이트는 스레드 생성 주변으로 이동해서는 안됩니다.

일반적으로이 기능을 Windows 서비스로 이동 한 다음 통신 할 수 있습니다 (MSMQ를 사용하여 통신합니다).

— 편집하다

여기서 구현에 대해 설명했습니다. ASP.NET MVC 웹 응용 프로그램의 큐 기반 백그라운드 처리

— 편집하다

이것이 스레드보다 더 나은 이유를 확장하려면 :

MSMQ를 사용하여 다른 서버와 통신 할 수 있습니다. 여러 컴퓨터에서 대기열에 쓸 수 있으므로 어떤 이유로 백그라운드 작업이 주 서버의 리소스를 너무 많이 사용하고 있다고 판단되면 아주 사소하게 이동할 수 있습니다.

또한하려고했던 모든 작업을 일괄 처리 할 수 ​​있습니다 (이메일 보내기 / 무엇이든).


답변

ASP.NET에서 빠르고 우선 순위가 낮은 비동기 작업을위한 일반적인 방법은 특히 리소스를 제한하려는 트래픽이 많은 시나리오에서 .NET 스레드 풀을 사용하는 것입니다.

또한 스레딩 구현이 숨겨져 있습니다. 자신의 스레드를 생성하기 시작하면 스레드도 제대로 관리해야합니다. 할 수 없다는 말이 아니라 왜 그 바퀴를 재발 명할까요?

성능이 문제가되고 스레드 풀이 제한 요소 (데이터베이스 연결, 나가는 네트워크 연결, 메모리, 페이지 시간 초과 등이 아님)임을 확인할 수있는 경우 스레드 풀 구성을 조정하여 더 많은 작업자 스레드, 더 많은 대기 요청을 허용합니다. 등

성능 문제가없는 경우 ASP.NET 요청 큐와의 경합을 줄이기 위해 새 스레드를 생성하도록 선택하는 것은 고전적인 조기 최적화입니다.

이상적으로는 로깅 작업을 수행하기 위해 별도의 스레드를 사용할 필요가 없습니다. 원래 스레드가 가능한 한 빨리 작업을 완료하도록 설정하면 MSMQ와 별도의 소비자 스레드 / 프로세스가 표시됩니다. 이 작업이 더 무겁고 구현해야 할 작업이 더 많다는 데 동의하지만 여기서는 내구성이 정말 필요합니다. 공유 된 인 메모리 대기열의 변동성은 빠르게 환영을 잃을 것입니다.


답변

QueueUserWorkItem을 사용하고 전염병을 피하는 것처럼 새 스레드를 생성하지 않아야합니다. ASP.NET이 동일한 ThreadPool을 사용하기 때문에 ASP.NET을 굶주 리지 않는 이유를 설명하는 비주얼을 보려면 두 손을 사용하여 6 개의 볼링 핀, 검 또는 비행 중에 무엇이든 유지하는 매우 숙련 된 요술쟁이를 상상해보십시오. 자신의 스레드를 만드는 것이 왜 나쁜지 시각적으로 보려면 고속도로 진입로를 많이 사용하여 차량이 조명을 사용하고 입구 수를 몇 초마다 1 개로 제한하는 대신 차량이 즉시 교통에 진입 할 수 있도록하는 러시아워에 시애틀에서 어떤 일이 발생하는지 상상해보십시오. . 마지막으로 자세한 설명은 다음 링크를 참조하십시오.

http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx

고마워, 토마스


답변

그 기사는 정확하지 않습니다. ASP.NET에는 ASP.NET 요청을 처리하기위한 자체 스레드 풀 (관리 작업자 스레드)이 있습니다. 이 풀은 일반적으로 수백 개의 스레드이며 ThreadPool 풀과는 별개입니다.이 풀은 프로세서의 일부 더 작은 배수입니다.

ASP.NET에서 ThreadPool을 사용하면 ASP.NET 작업자 스레드를 방해하지 않습니다. ThreadPool을 사용하는 것이 좋습니다.

메시지를 로깅하고 생산자 / 소비자 패턴을 사용하여 해당 스레드에 로그 메시지를 전달하는 단일 스레드를 설정하는 것도 허용됩니다. 이 경우 스레드의 수명이 길기 때문에 로깅을 실행하려면 단일 새 스레드를 만들어야합니다.

모든 메시지에 대해 새 스레드를 사용하는 것은 확실히 과잉입니다.

로깅에 대해서만 이야기하는 경우 또 다른 대안은 log4net과 같은 라이브러리를 사용하는 것입니다. 별도의 스레드에서 로깅을 처리하고 해당 시나리오에서 발생할 수있는 모든 컨텍스트 문제를 처리합니다.


답변

기사가 틀렸다고 말하고 싶습니다. 대규모 .NET 상점을 운영하는 경우 ThreadPool 설명서의 한 문장을 기반으로 여러 앱과 여러 웹 사이트 (별도의 앱 풀 사용)에서 안전하게 풀을 사용할 수 있습니다 .

프로세스 당 하나의 스레드 풀이 있습니다.
스레드 풀의 기본 크기는 사용 가능한 프로세서 당 250 개의 작업자 스레드와 1000 개의 I / O 완료 스레드입니다. 스레드 풀의 스레드 수는 SetMaxThreads 메서드를 사용하여 변경할 수 있습니다. 각 스레드는 기본 스택 크기를 사용하고 기본 우선 순위로 실행됩니다.