이 질문 후 비동기를 사용할 때 편안함을 느낍니다. ASP.NET MVC에서의 작업. 그래서, 나는 그것에 두 개의 블로그 게시물을 썼다.
ASP.NET MVC의 비동기 작업에 대한 오해가 너무 많습니다.
나는 항상이 문장을 듣는다.작업이 비동기 적으로 실행되는 경우 응용 프로그램의 확장 성이 향상 될 수 있습니다.
그리고 저는 이런 종류의 문장들도 많이 들었습니다.방대한 양의 트래픽이있는 경우 비동기 적으로 쿼리를 수행하지 않는 것이 좋습니다. 하나의 요청을 처리하기 위해 2 개의 추가 스레드를 사용하면 다른 들어오는 요청에서 리소스가 이동됩니다.
나는이 두 문장이 일치하지 않는다고 생각한다.
스레드 풀이 ASP.NET에서 작동하는 방법에 대한 많은 정보가 없지만 스레드 풀의 스레드 수가 제한되어 있다는 것을 알고 있습니다. 따라서 두 번째 문장은이 문제와 관련이 있어야합니다.
ASP.NET MVC의 비동기 작업이 .NET 4의 ThreadPool에서 스레드를 사용하는지 알고 싶습니다.
예를 들어 AsyncController를 구현하면 앱 구조는 어떻게 구성됩니까? 막대한 트래픽이 발생하면 AsyncController를 구현하는 것이 좋습니다.
이 검은 커튼을 눈 앞에서 가져 가서 ASP.NET MVC 3 (NET 4)에서 비동기성에 대한 거래를 설명 할 수있는 사람이 있습니까?
편집하다:
나는이 문서를 거의 수백 번 읽었으며 주된 거래를 이해하고 있지만 아직도 너무 모순 된 주석이 있기 때문에 혼란 스럽다.
편집하다:
아래처럼 컨트롤러 액션이 있다고 가정 해 봅시다 (AsyncController
그래도):
public ViewResult Index() {
Task.Factory.StartNew(() => {
//Do an advanced looging here which takes a while
});
return View();
}
보시다시피, 저는 작업을 해고 잊어 버립니다. 그런 다음 기다리지 않고 바로 돌아갑니다.
이 경우 스레드 풀에서 스레드를 사용해야합니까? 그렇다면, 완료 후, 그 스레드는 어떻게됩니까? 그렇다GC
완료되면 바로 들어오고 정리할 수 있습니까?
편집하다:
@ Darin의 답은 데이터베이스와 대화하는 비동기 코드 예제입니다.
public class FooController : AsyncController {
//EF 4.2 DbContext instance
MyContext _context = new MyContext();
public void IndexAsync() {
AsyncManager.OutstandingOperations.Increment(3);
Task<IEnumerable<Foo>>.Factory.StartNew(() => {
return
_context.Foos;
}).ContinueWith(t => {
AsyncManager.Parameters["foos"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
Task<IEnumerable<Bars>>.Factory.StartNew(() => {
return
_context.Bars;
}).ContinueWith(t => {
AsyncManager.Parameters["bars"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
Task<IEnumerable<FooBar>>.Factory.StartNew(() => {
return
_context.FooBars;
}).ContinueWith(t => {
AsyncManager.Parameters["foobars"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
}
public ViewResult IndexCompleted(
IEnumerable<Foo> foos,
IEnumerable<Bar> bars,
IEnumerable<FooBar> foobars) {
//Do the regular stuff and return
}
}
여기에우수한 기사ASP.NET (비동기 컨트롤러가 기본적으로 나타내는 것)에서 비동기 처리를 더 잘 이해하기 위해 읽는 것이 좋습니다.
먼저 표준 동기 동작을 고려해 보겠습니다.
public ActionResult Index()
{
// some processing
return View();
}
이 동작에 대한 요청이 이루어지면 스레드 풀에서 스레드가 그려지고이 동작의 본문이이 스레드에서 실행됩니다. 따라서이 작업 내에서 처리가 느린 경우 전체 처리에 대해이 스레드를 차단하므로이 스레드를 다른 요청을 처리하기 위해 다시 사용할 수 없습니다. 요청 실행이 끝나면 스레드는 스레드 풀로 반환됩니다.
이제 비동기 패턴의 예를 들어 봅시다.
public void IndexAsync()
{
// perform some processing
}
public ActionResult IndexCompleted(object result)
{
return View();
}
요청이 Index 액션으로 보내지면 스레드 풀에서 스레드가 그려지고IndexAsync
메소드가 실행됩니다. 이 메소드의 본문이 실행되면 스레드가 스레드 풀로 반환됩니다. 그런 다음 표준 사용AsyncManager.OutstandingOperations
일단 비동기 작업의 완료를 알게되면 다른 스레드가 스레드 풀에서 끌어오고IndexCompleted
액션이 실행되고 결과가 클라이언트에 렌더링됩니다.
이 패턴에서 볼 수있는 것은 단일 클라이언트 HTTP 요청이 두 개의 다른 스레드에 의해 실행될 수 있다는 것입니다.
이제 흥미로운 부분은IndexAsync
방법. 내부에 블로킹 연산이 있다면, 작업자 스레드를 블로킹하기 때문에 비동기 컨트롤러의 모든 목적을 완전히 낭비하고 있습니다 (이 동작의 본문은 스레드 풀에서 가져온 스레드에서 실행됨을 기억하십시오).
그렇다면 비동기 컨트롤러를 언제쯤 활용할 수 있습니까?
IMHO I / O 집약적 인 작업 (예 : 원격 서비스에 대한 데이터베이스 및 네트워크 호출)이있을 때 가장 많이 얻을 수 있습니다. CPU 집중적 인 작업을 수행하는 경우 비동기 작업으로 많은 이점을 얻을 수 없습니다.
그렇다면 왜 I / O 집약적 인 운영에서 이점을 얻을 수 있습니까? 우리가 사용할 수 있기 때문에I / O 완료 포트. IOCP는 전체 작업을 실행하는 동안 서버에서 스레드 나 리소스를 사용하지 않기 때문에 매우 강력합니다.
어떻게 작동합니까?
우리가 원격 웹 페이지의 내용을 다운로드하려면WebClient.DownloadStringAsync방법. 운영 체제 내에서 IOCP를 등록하고 즉시 반환하는이 메서드를 호출합니다. 전체 요청을 처리하는 동안 서버에서 스레드가 소비되지 않습니다. 모든 것은 원격 서버에서 발생합니다. 이것은 많은 시간이 걸릴 수 있지만 작업자 스레드를 위험에 빠뜨리지 않으면 서 상관하지 않습니다. 응답이 수신되면 IOCP 신호가 보내지고 스레드 풀에서 스레드가 그려지고이 스레드에서 콜백이 실행됩니다. 그러나 전체 과정에서 볼 수 있듯이 스레드를 독점하지 않았습니다.
FileStream.BeginRead, SqlCommand.BeginExecute 등과 같은 메서드에서도 마찬가지입니다.
여러 데이터베이스 호출을 병렬 처리하는 것은 어떻습니까? 순서대로 4 개의 블록킹 데이터베이스 호출을 수행 한 동기 콘트롤러 조치가 있다고 가정하십시오. 각 데이터베이스 호출에 200ms가 걸리면 컨트롤러 동작이 실행되는 데 약 800ms가 걸릴 것이라고 계산하기 쉽습니다.
이러한 호출을 순차적으로 실행할 필요가없는 경우이를 병렬화하면 성능이 향상됩니까?
그것은 대답하기 쉽지 않은 큰 질문입니다. 어쩌면 네, 아마도 아닐 수도 있습니다. 그것은 전적으로 데이터베이스 호출을 구현하는 방법에 달려 있습니다. 앞에서 설명한 비동기 컨트롤러와 I / O 완료 포트를 사용하면 작업자 스레드를 독점하지 않으므로이 컨트롤러 동작과 다른 작업의 성능을 향상시킬 수 있습니다.
반면 (스레드 풀에서 스레드에 대해 수행되는 데이터베이스 호출을 차단하여) 구현을 잘못 수행하면 기본적으로이 작업의 실행 시간이 약 200ms 가량 줄어들지 만 작업자 스레드가 4 번 소모되므로 풀에서 풀을 처리하지 못해 배고파 질 수있는 다른 요청의 성능이 저하되었을 수 있습니다.
따라서 매우 어렵습니다. 응용 프로그램에 대한 광범위한 테스트를 수행 할 준비가되지 않았다면 비동기 컨트롤러를 구현하지 마십시오. 이익보다 많은 피해를 입을 가능성이 있습니다. 그러한 이유가있는 경우에만 구현하십시오. 예를 들어 표준 동기식 컨트롤러 작업이 광범위한 부하 테스트 및 측정 과정을 거친 후 응용 프로그램의 병목 현상임을 확인한 경우입니다.
이제 예제를 살펴 보겠습니다.
public ViewResult Index() {
Task.Factory.StartNew(() => {
//Do an advanced looging here which takes a while
});
return View();
}
Index 액션에 대한 요청을 받으면 스레드 풀에서 스레드를 가져 와서 본문을 실행하지만 해당 본문은 다음을 사용하여 새 작업 만 예약합니다.TPL. 따라서 작업 실행이 끝나고 스레드가 스레드 풀로 반환됩니다. 그것을 제외하고,TPL스레드 풀의 스레드를 사용하여 처리를 수행합니다. 따라서 원래 스레드가 스레드 풀로 반환 된 경우에도이 풀에서 다른 스레드를 가져 와서 작업의 본문을 실행했습니다. 그래서 당신은 당신의 소중한 수영장에서 2 개의 실을 위험에 빠뜨립니다.
이제 다음 사항을 고려해 보겠습니다.
public ViewResult Index() {
new Thread(() => {
//Do an advanced looging here which takes a while
}).Start();
return View();
}
이 경우 우리는 수동으로 스레드를 생성합니다. 이 경우 인덱스 작업 본문 실행에 약간 시간이 걸릴 수 있습니다 (새 스레드를 생성하면 기존 풀에서 스레드를 그리는 것보다 비용이 많이 듭니다). 그러나 고급 로깅 작업의 실행은 풀의 일부가 아닌 스레드에서 수행됩니다. 따라서 다른 요청을 처리 할 때 무료로 남아있는 스레드를 위험에 빠뜨리지는 않습니다.
System.Threading.Task
) 내부에서 실행 중IndexAsync
방법. 이러한 작업 내에서 db에 대한 호출을 서버에 수행합니다. 그래서, 모두 I / O 집약적 인 작업입니다. 맞습니까? 이 경우 4 개의 별도 스레드를 만들거나 스레드 풀에서 4 개의 개별 스레드를 가져 옵니까? 멀티 코어 머신을 사용한다고 가정하면 병렬로 실행될 것입니다. 그렇습니까? - tugberkSqlCommand.ExecuteReader
이것이 막히는 호출이기 때문에 당신은 모든 것을 낭비하고 있습니다. 이 호출이 실행되는 스레드를 차단하고이 스레드가 풀의 스레드 인 경우 매우 나쁩니다. I / O 완료 포트를 사용하는 경우에만 이점이 있습니다.SqlCommand.BeginExecuteReader
. IOCP를 사용하지 않는다면 비동기 컨트롤러를 사용하지 마십시오. 응용 프로그램의 전반적인 성능에 비해 많은 피해를 입 힙니다. - Darin Dimitrov_context.Foo
당신은 실제로 아무 것도 실행하지 않습니다. 당신은 단지 표현 트리를 만들고 있습니다. 이것에 매우 조심하십시오. 쿼리 실행은 결과 집합을 열거하기 시작할 때만 연기됩니다. 그리고 이것이보기에서 발생하면 성능에 치명적일 수 있습니다. 열심히 EF 쿼리 추가를 실행하려면.ToList()
결국. - Darin Dimitrov
예 - 모든 스레드는 스레드 풀에서옵니다. MVC 응용 프로그램은 이미 다중 스레드입니다. 요청이 들어 오면 새 스레드가 풀에서 가져와 요청을 처리하는 데 사용됩니다. 이 스레드는 요청이 완전히 서비스되고 완료 될 때까지 (다른 요청으로부터) '잠김'상태가됩니다. 풀에서 사용할 수있는 스레드가없는 경우 요청은 사용할 수있을 때까지 대기해야합니다.
비동기식 컨트롤러를 사용하는 경우에도 여전히 풀에서 스레드를 가져 오지만 요청을 처리하는 동안 스레드는 스레드를 포기할 수 있으며, 어떤 일이 일어날 때까지 대기하면서 (그리고 해당 스레드는 다른 요청에 제공 될 수 있음) 원래 요청에 스레드가 필요할 때 다시 풀에서 하나를 얻습니다.
차이점은 장기 실행 요청이 많은 경우 (스레드가 무언가의 응답을 기다리고있는 경우) 풀의 스레드가 부족하여 기본 요청도 처리 할 수 있다는 것입니다. 비동기 컨트롤러가있는 경우 더 이상 스레드가 없지만 대기중인 스레드는 풀로 반환되고 다른 요청을 처리 할 수 있습니다.
에이거의실생활의 예 ... 버스를 타는 것, 승차하기를 기다리는 5 명의 사람들이 있다고 생각해보십시오. 첫 번째 승차, 지불 및 앉기 (운전 기사가 요청을 서비스), 승차 (운전 기사가 요청을 서비스하고 있음)하지만 너의 돈을 발견하라. 주머니에서 웅크 리고 운전자가 당신을 포기하고 (요청을 서비스하는) 다음 두 사람을 얻습니다. 운전 기사가 당신을 다시 만날 돈을 발견하면 (귀하의 요청을 완료하십시오) - 다섯 번째 사람까지 기다려야합니다 당신은 일을 끝내고 있지만 절반을 먹는 동안 세 번째와 네 번째 사람들이 봉사했습니다. 즉, 드라이버는 수영장에서 유일한 스레드이며 승객은 요청입니다. 2 명의 운전사가있는 경우에 그것이 작동 할 방법을 쓰는 것은 너무 복잡했다 그러나 상상할 수있다 ...
비동기 컨트롤러가 없다면, 돈을 찾은 동안 승객이 오래 기다려야 할 것이고 버스 운전 기사는 아무런 노력을하지 않을 것입니다.
결론적으로, 많은 사람들이 돈이 어디에 있는지를 모르는 경우 (즉, 운전자가 요청한 것에 응답하는 데 오랜 시간이 걸리는 경우) 비동기 컨트롤러가 요청 처리량에 도움을 줄 수있어 일부 프로세스 속도가 빨라집니다. Aysnc 컨트롤러가 없으면 전방에있는 사람이 완전히 처리 될 때까지 모두 기다립니다. 그러나 MVC에서 단일 버스에 많은 버스 드라이버가있어서 async가 자동 선택이 아님을 잊지 마십시오.
여기에는 두 가지 개념이 있습니다. 우선 우리는 코드를 병렬로 실행하여 다른 스레드에서 코드를 더 빨리 실행하거나 일정을 잡아서 사용자를 기다리지 않게 할 수 있습니다. 당신이 가진 예
public ViewResult Index() {
Task.Factory.StartNew(() => {
//Do an advanced looging here which takes a while
});
return View();
}
두 번째 범주에 속합니다. 사용자는 더 빠른 응답을 얻지 만 서버의 총 작업량은 더 높습니다. 왜냐하면 동일한 작업 + 스레딩을 처리해야하기 때문입니다.
이것의 또 다른 예는 다음과 같습니다.
public ViewResult Index() {
Task.Factory.StartNew(() => {
//Make async web request to twitter with WebClient.DownloadString()
});
Task.Factory.StartNew(() => {
//Make async web request to facebook with WebClient.DownloadString()
});
//wait for both to be ready and merge the results
return View();
}
요청이 병렬로 실행되기 때문에 사용자는 어디서나 한 번씩 기다릴 필요가 없습니다. 그러나 스레드 대기 상태에서 많은 스레드에서 코드를 실행하기 때문에 직렬로 실행하는 것보다 더 많은 리소스를 사용한다는 것을 알아야합니다.
이것은 클라이언트 시나리오에서 완벽하게 좋습니다. 그리고 새로운 작업 (다른 스레드에서 실행)에 동기식 장기 실행 코드를 래핑하여 UI를 응답 성있게 유지하거나 더 빠르게 만들 수 있습니다. 스레드는 여전히 전체 기간 동안 사용됩니다. 로드가 많은 서버에서는 실제로 더 많은 리소스를 사용하기 때문에 역효과를 낼 수 있습니다.이것은 사람들이 당신에게 경고 한 것입니다.
MVC의 비동기 컨트롤러에는 또 다른 목표가 있습니다. 여기에서 요점은 스레드가 아무 것도하지 않고 앉아있는 것을 피하는 것입니다 (이는 확장 성을 손상시킬 수 있습니다). API의 호출에 비동기 메소드가있는 경우에만 중요합니다. WebClient.DowloadStringAsync ()와 같습니다.
핵심은 웹 요청이 끝날 때까지 새 요청을 처리하도록 스레드를 반환 할 수 있으며, 동일한 또는 새 스레드를 가져 오는 콜백을 호출하고 요청을 마무리합니다.
비동기와 병렬의 차이점을 이해하시기 바랍니다. 병렬 코드를 쓰레드가 앉아서 결과를 기다리는 코드로 생각하십시오. 비동기 코드는 코드가 완료되었을 때 알림을 받고 다시 작업 할 수있는 코드이지만 스레드는 다른 작업을 수행 할 수 있습니다.
응용 분야양철통작업이 비동기 적으로 실행되면 확장 성이 향상되지만추가 작업을 처리 할 수있는 자원이있는 경우에만.
비동기 작업은 기존 작업이 진행 중이므로 결코 작업을 차단하지 않도록합니다. ASP.NET에는 여러 요청을 나란히 실행할 수있는 비동기 모델이 있습니다. 요청을 대기열에 넣고 FIFO로 처리하는 것이 가능하지만 수백 개의 요청이 대기 중이고 각 요청이 처리하는 데 100ms가 걸리면 확장이 잘되지 않습니다.
방대한 양의 트래픽이있는 경우할 수있다비동기 적으로 쿼리를 수행하지 않는 것이 더 좋으며,요청을 처리 할 추가 리소스가 없으므로. 여분의 자원이 없으면 요청은 대기열에 올려 지거나 지수 적으로 길거나 완전히 실패하게됩니다.이 경우 비동기 오버 헤드 (뮤텍스 및 컨텍스트 전환 작업)가 아무 것도주지 못합니다.
ASP.NET에 관한 한, 선택의 여지가 없습니다. 비동기 모델을 사용합니다. 왜냐하면 이것이 서버 - 클라이언트 모델에 적합한 이유이기 때문입니다. 비동기 패턴을 사용하여 자체적으로 코드를 작성하여 더 나은 확장을 시도하는 경우 모든 요청간에 공유되는 리소스를 관리하지 않는 한 이미 래핑되었으므로 실제로 개선되지 않습니다. 다른 것을 차단하지 않는 비동기 프로세스에서
궁극적으로 시스템에서 병목 현상을 일으키는 원인을 실제로 파악할 때까지 모두 주관적입니다. 때로는 비동기 패턴이 도움이되는 곳에서 (대기중인 리소스 차단을 방지하여) 분명합니다. 궁극적으로 시스템을 측정하고 분석하는 것만으로 효율성을 얻을 수있는 위치를 알 수 있습니다.
편집하다:
귀하의 예에서Task.Factory.StartNew
호출은 .NET 스레드 풀에서 작업을 대기열에 넣습니다. 쓰레드 풀 쓰레드의 본질은 (많은 쓰레드를 생성 / 파괴하는 비용을 피하기 위해) 재사용되어야한다. 작업이 완료되면 스레드는 다른 요청에 의해 다시 사용하기 위해 풀로 해제됩니다. 작업에 일부 객체를 만들지 않으면 가비지 수집기가 실제로 관여하지 않습니다.이 경우 일반 작업에 따라 수집됩니다 범위 지정).
ASP.NET과 관련하여 여기에는 특별한 작업이 없습니다. ASP.NET 요청은 비동기 작업과 관련없이 완료됩니다. 스레드 풀이 포화 된 경우 (즉, 요청을 처리 할 수있는 스레드가없고 풀 설정에서 더 많은 스레드를 만들 수없는 경우) 요청이 차단 된 경우에만 문제가 될 수 있습니다작업을 시작하기 위해 대기 중풀 스레드가 사용 가능해질 때까지
Task.Factory.StartNew
호출은 .NET 스레드 풀에서 작업을 대기열에 넣습니다.. 이 컨텍스트에서 올바른 것은 다음 중 어느 것입니까?1-)새로운 스레드를 생성하고 완료되면 해당 스레드는 다시 스레드 풀로 돌아가서 다시 재사용 대기합니다.2-스레드 풀에서 스레드를 가져오고 해당 스레드는 다시 스레드 풀로 돌아가서 다시 재사용 대기합니다.삼-)가장 효율적인 접근법을 취하고 그 중 하나를 수행 할 수 있습니다. - tugberk
예, 스레드 풀의 스레드를 사용합니다. 실제로 MSDN의 훌륭한 가이드가 귀하의 모든 질문 등을 해결할 것입니다. 나는 그것이 과거에 아주 유용하다는 것을 알았다. 확인 해봐!
http://msdn.microsoft.com/en-us/library/ee728598.aspx
한편 비동기 코드에 대한 의견 + 제안은 소금 한 알을 사용하여 가져와야합니다. 처음에는 비동기로 만드는 것이 반드시 확장 성을 향상시키는 것은 아니며 어떤 경우에는 애플리케이션 규모를 더욱 악화시킬 수 있습니다. "거대한 양의 트래픽"에 관해 게시 한 다른 의견은 특정 상황에서만 정확합니다. 그것은 당신의 작업이하는 일과 그들이 시스템의 다른 부분과 어떻게 상호 작용하는지에 달려 있습니다.
간단히 말해서, 많은 사람들이 비동기에 대한 의견이 많지만 상황에 맞게 정확하지 않을 수 있습니다. 나는 당신의 정확한 문제에 초점을 맞추고, 비동기 컨트롤러 등이 실제로 무엇을 다루는 지 알아보기 위해 기본적인 성능 테스트를 할 것입니다.너의신청.
GC
와서 끝나면 바로 청소하여 내 앱에 메모리 누수가 없도록하는 등의 작업을합니다. 그 부분에 대한 어떤 생각. - tugberk
우선 MVC가 아니라 스레드 풀을 유지 관리하는 IIS입니다. 따라서 MVC 또는 ASP.NET 응용 프로그램에 대한 요청은 스레드 풀에서 유지 관리되는 스레드에서 제공됩니다. 앱을 Asynch로 만들 때만 다른 스레드에서이 작업을 호출하고 스레드를 즉시 해제하여 다른 요청을 수행 할 수 있습니다.
나는 세부 비디오 (http://www.youtube.com/watch?v=wvg13n5V0V0/"MVC Asynch 컨트롤러 및 스레드 기아"). MVC에서 스레드 부족이 발생하는 방법과 MVC Asynch 컨트롤러를 사용하여 스레드 부족을 최소화하는 방법을 보여줍니다. 또한 perfmon을 사용하여 요청 큐를 측정하여 MVC asynch에 대해 요청 큐가 감소하는 방식을 볼 수 있습니다. Synch 작업에있어서 최악의 경우.