IT박스

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

itboxs 2020. 8. 4. 07:40
반응형

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


ASP.NET에서도 ThreadPool을 사용하는 것이 중요하지 않은 단기 백그라운드 작업에 대해 모범 사례로 간주되었다는 인상을 항상 받아 왔지만 이 기사에서는 다른 방법으로 제안했습니다. 인수는 ASP.NET 관련 요청을 처리하기 위해 ThreadPool을 남겨 두어야한다는 것입니다.

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

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

그리고 기사 는 대신 다음과 같이 명시 적으로 스레드를 만들 것을 제안합니다.

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

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

내 질문은, 기사의 조언이 정확합니까?

사이트에 너무 많은 트래픽이 발생하여 ThreadPool이 가득 차면 대역 외로 나가는 것이 좋거나 전체 ThreadPool이 리소스 제한에 도달했음을 암시합니다. 자신의 스레드를 시작하려고해서는 안됩니까?

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


여기에있는 다른 답변은 가장 중요한 요점을 벗어난 것으로 보입니다.

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

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

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

그러나 실제로이 기아를 유발하기에 충분한 작업 항목을 큐에 넣으면 스레드 풀이 고갈 되어야 합니다! 문자 그대로 수백 개의 CPU를 많이 사용하는 작업을 동시에 실행하는 경우 컴퓨터에 이미 과부하가 걸렸을 때 ASP.NET 요청을 처리하기 위해 다른 작업자 스레드를 사용하는 것이 좋을까요? 이 상황이 발생하면 완전히 재 설계해야합니다!

ASP.NET에서 멀티 스레드 코드가 부적절하게 사용되는 것을 보거나 듣는 대부분의 시간은 CPU를 많이 사용하는 작업이 아닙니다. I / O 바운드 작업을 큐잉하기위한 것입니다. 그리고 I / O 작업을하려면 I / O 스레드 (I / O 완료 포트)를 사용해야합니다.

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

이러한 방법 은을 사용 ThreadPool하지만 ASP.NET 요청을 방해 하지 않는 IOCP를 사용 합니다. 그것들은 I / O 시스템의 인터럽트 신호에 의해 "깨어날"수있는 특별한 종류의 경량 스레드입니다. 그리고 ASP.NET 응용 프로그램에는 일반적으로 각 작업자 스레드마다 하나의 I / O 스레드가 있으므로 모든 단일 요청에 대해 하나의 비동기 작업이 대기 될 수 있습니다. 이는 실질적으로 성능 저하없이 수백 개의 비동기 작업입니다 (I / O 하위 시스템을 유지할 수 있다고 가정). 당신이 필요로하는 것 이상의 방법입니다.

비동기 델리게이트 는 이런 식으로 작동하지 않는다는 점을 명심하십시오 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) [T] o 요약하면 ASP.NET 스레드가 고갈 될 염려가 없으며 여기에 문제가 있다고 생각되면 알려 주시면 처리하겠습니다.

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

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

1) QueueUserWorkItem과 비교할 때 비용이 많이 듭니다 ... 그런데 CLR보다 더 나은 ThreadPool을 작성할 수 있다면 Microsoft에서 일자리를 신청하는 것이 좋습니다. .


웹 사이트는 스폰 스레드 주위를 돌아서는 안됩니다.

일반적으로이 기능을 Windows 서비스로 옮긴 다음 통신합니다 (MSMQ를 사용하여 통신합니다).

-- 편집하다

I described an implementation here: Queue-Based Background Processing in ASP.NET MVC Web Application

-- Edit

To expand why this is even better than just threads:

Using MSMQ, you can communicate to another server. You can write to a queue across machines, so if you determine, for some reason, that your background task is using up the resources of the main server too much, you can just shift it quite trivially.

It also allows you to batch-process whatever task you were trying to do (send emails/whatever).


I definitely think that general practice for quick, low-priority asynchronous work in ASP.NET would be to use the .NET thread pool, particularly for high-traffic scenarios as you want your resources to be bounded.

Also, the implementation of threading is hidden - if you start spawning your own threads, you have to manage them properly as well. Not saying you couldn't do it, but why reinvent that wheel?

If performance becomes an issue, and you can establish that the thread pool is the limiting factor (and not database connections, outgoing network connections, memory, page timeouts etc) then you tweak the thread pool configuration to allow more worker threads, higher queued requests, etc.

If you don't have a performance problem then choosing to spawn new threads to reduce contention with the ASP.NET request queue is classic premature optimization.

Ideally you wouldn't need to use a separate thread to do a logging operation though - just enable the original thread to complete the operation as quickly as possible, which is where MSMQ and a separate consumer thread / process come in to the picture. I agree that this is heavier and more work to implement, but you really need the durability here - the volatility of a shared, in-memory queue will quickly wear out its welcome.


You should use QueueUserWorkItem, and avoid creating new threads like you would avoid the plague. For a visual that explains why you won't starve ASP.NET, since it uses the same ThreadPool, imagine a very skilled juggler using two hands to keep a half dozen bowling pins, swords, or whatever in flight. For a visual of why creating your own threads is bad, imagine what happens in Seattle at rush hour when heavily used entrance ramps to the highway allow vehicles to enter traffic immediately instead of using a light and limiting the number of entrances to one every few seconds. Finally, for a detailed explanation, please see this link:

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

Thanks, Thomas


That article is not correct. ASP.NET has it's own pool of threads, managed worker threads, for serving ASP.NET requests. This pool is usually a few hundred threads and is separate from the ThreadPool pool, which is some smaller multiple of processors.

Using ThreadPool in ASP.NET will not interfere with ASP.NET worker threads. Using ThreadPool is fine.

It would also be acceptable to setup a single thread which is just for logging messages and using producer/consumer pattern to pass logs messages to that thread. In that case, since the thread is long-lived, you should create a single new thread to run the logging.

Using a new thread for every message is definitely overkill.

Another alternative, if you're only talking about logging, is to use a library like log4net. It handles logging in a separate thread and takes care of all the context issues that could come up in that scenario.


I'd say the article is wrong. If you're running a large .NET shop you can safely use the pool across multiple apps and multiple websites (using seperate app pools), simply based on one statement in the ThreadPool documentation:

There is one thread pool per process. The thread pool has a default size of 250 worker threads per available processor, and 1000 I/O completion threads. The number of threads in the thread pool can be changed by using the SetMaxThreads method. Each thread uses the default stack size and runs at the default priority.


I was asked a similar question at work last week and I'll give you the same answer. Why are you multi threading web applications per request? A web server is a fantastic system optimized heavily to provide many requests in a timely fashion (i.e. multi threading). Think of what happens when you request almost any page on the web.

  1. A request is made for some page
  2. Html is served back
  3. The Html tells the client to make further requets (js, css, images, etc..)
  4. Further information is served back

You give the example of remote logging, but that should be a concern of your logger. An asynchronous process should be in place to receive messages in a timely fashion. Sam even points out that your logger (log4net) should already support this.

Sam is also correct in that using the Thread Pool on the CLR will not cause issues with the thread pool in IIS. The thing to be concerned with here though, is that you are not spawning threads from a process, you are spawning new threads off of IIS threadpool threads. There is a difference and the distinction is important.

Threads vs Process

Both threads and processes are methods of parallelizing an application. However, processes are independent execution units that contain their own state information, use their own address spaces, and only interact with each other via interprocess communication mechanisms (generally managed by the operating system). Applications are typically divided into processes during the design phase, and a master process explicitly spawns sub-processes when it makes sense to logically separate significant application functionality. Processes, in other words, are an architectural construct.

By contrast, a thread is a coding construct that doesn't affect the architecture of an application. A single process might contains multiple threads; all threads within a process share the same state and same memory space, and can communicate with each other directly, because they share the same variables.

Source


I do not agree with the referenced article(C#feeds.com). It is easy to create a new thread but dangerous. The optimal number of active threads to run on a single core is actually surprisingly low - less than 10. It is way too easy to cause the machine to waste time switching threads if threads are created for minor tasks. Threads are a resource that REQUIRE management. The WorkItem abstraction is there to handle this.

There is a trade off here between reducing the number of threads available for requests and creating too many threads to allow any of them to process efficiently. This is a very dynamic situation but I think one that should be actively managed (in this case by the thread pool) rather than leaving it to the processer to stay ahead of the creation of threads.

Finally the article makes some pretty sweeping statements about the dangers of using the ThreadPool but it really needs something concrete to back them up.


Whether or not IIS uses the same ThreadPool to handle incoming requests seems hard to get a definitive answer to, and also seems to have changed over versions. So it would seem like a good idea not to use ThreadPool threads excessively, so that IIS has a lot of them available. On the other hand, spawning your own thread for every little task seems like a bad idea. Presumably, you have some sort of locking in your logging, so only one thread could progress at a time, and the rest would just take turns getting scheduled and unscheduled (not to mention the overhead of spawning a new thread). Essentially, you run into the exact problems the ThreadPool was designed to avoid.

It seems that a reasonable compromise would be for your app to allocate a single logging thread that you could pass messages to. You would want to be careful that sending messages is as fast as possible so that you don't slow down your app.


You can use Parallel.For or Parallel.ForEach and define the limit of possible threads you want to allocate to run smoothly and prevent pool starvation.

However, being run in background you will need to use pure TPL style below in ASP.Net web application.

var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;

ParallelOptions po = new ParallelOptions();
            po.CancellationToken = ts.Token;
            po.MaxDegreeOfParallelism = 6; //limit here

 Task.Factory.StartNew(()=>
                {                        
                  Parallel.ForEach(collectionList, po, (collectionItem) =>
                  {
                     //Code Here PostLog(logEvent);
                  }
                });

참고URL : https://stackoverflow.com/questions/1325718/using-threadpool-queueuserworkitem-in-asp-net-in-a-high-traffic-scenario

반응형