2014-01-28 4 views
40

ASP.NET 응용 프로그램에서 완료하는 데 많은 시간이 소요되는 메서드가 있습니다. 이 메서드는 사용자가 제공하는 캐시 상태 및 매개 변수에 따라 한 번의 사용자 요청 중에 최대 3 회까지 호출 될 수 있습니다. 각 통화는 완료하는 데 약 1-2 초가 걸립니다. 메서드 자체는 서비스에 대한 동기 호출이며 구현을 재정의 할 가능성이 없습니다.
그래서 서비스에 대한 동기 호출은 다음과 같이 보입니다 :비동기 호출로 동기 코드 래핑

public OutputModel Calculate(InputModel input) 
{ 
    // do some stuff 
    return Service.LongRunningCall(input); 
} 

그리고 방법의 사용을한다 (참고, 방법의 호출은 두 번 이상 발생할 수 있습니다) :

private void MakeRequest() 
{ 
    // a lot of other stuff: preparing requests, sending/processing other requests, etc. 
    var myOutput = Calculate(myInput); 
    // stuff again 
} 

이 방법의 동시 작업을 제공하기 위해 구현을 내 측면에서 변경하려고 시도했으며, 여기까지 왔습니다.

public async Task<OutputModel> CalculateAsync(InputModel input) 
{ 
    return await Task.Run(() => 
    { 
     return Calculate(input); 
    }); 
} 

사용 (코드 "다른 물건을 할"의 일부 서비스에 대한 호출과 동시에 실행) :

private async Task MakeRequest() 
{ 
    // do some stuff 
    var task = CalculateAsync(myInput); 
    // do other stuff 
    var myOutput = await task; 
    // some more stuff 
} 

내 질문은 다음과 같다. ASP.NET 응용 프로그램에서 실행 속도를 높이기 위해 올바른 방법을 사용합니까? 아니면 비동기식으로 동기식 코드를 실행하려고 불필요한 작업을 수행합니까? 아무도 왜 두 번째 접근 방식이 ASP.NET에서 옵션이 아닌지 설명 할 수 있습니까 (실제로 그렇지 않은 경우)? 또한 그러한 접근법이 적용 가능하다면, 우리가 그 순간에 수행 할 수있는 유일한 호출 인 경우 그러한 메소드를 비동기 적으로 호출해야합니다 (완료를 기다리는 동안 수행해야 할 다른 작업이없는 경우).
이 주제의 그물에있는 대부분의 기사는 이미 awaitable 방법을 제공하는 코드를 사용하여 async-await 접근 방식을 사용하는 것을 다루지 만 저의 경우는 아닙니다. Here 내 사건을 설명하는 좋은 기사, 병렬 통화의 상황을 설명하지 않는, 동기화 전화를 감싸는 옵션을 거부하지만 내 생각에 내 상황은 정확하게 그것을 할 기회입니다.
도움과 팁에 대해 미리 감사드립니다.

답변

50

두 가지 유형의 동시성을 구분해야합니다. 비동기 동시성은 여러 비동기 작업이 진행 중일 때 발생합니다. 각 작업이 비동기 적이기 때문에 실제로는 스레드을 사용하지 않습니다. 병렬 동시성은 각각 별도의 작업을 수행하는 여러 스레드가있는 경우입니다.

방법 자체는 서비스에 대한 동기 호출과 구현을 오버라이드 (override) 할 가능성이 없다 :

됩니다 먼저 할 일은 이러한 가정을 다시 평가합니다. 당신의 "서비스"는 서비스 나 I /이다 아무것도 경우

그런 다음 가장 좋은 방법은 그것을 위해 비동기 API를 작성하는 것입니다, O는 바인딩.

"서비스"는 웹 서버와 동일한 컴퓨터에서 실행해야하는 CPU 바운드 작업이라는 가정하에 진행할 것입니다.

내가 빨리 실행 요청을해야합니다

그런 경우에는, 그 다음 일을 평가하는 다른 가정이다.

당신이해야 할 일은 무엇입니까? 대신 요청을 시작하고 사용자가 처리하는 동안 다른 작업을 수행 할 수 있도록 프런트 엔드를 변경할 수 있습니까?

예, 실제로 개별 요청을 더 빠르게 실행해야한다고 가정합니다.

이 경우 웹 서버에서 병렬 코드를 실행해야합니다. 이것은 병렬 코드가 ASP.NET이 다른 요청을 처리하는 데 필요할 수있는 스레드를 사용하고 스레드를 제거/추가함으로써 ASP.NET 스레드 풀 경험을 없애기 때문에 일반적으로 일반적으로 권장되지 않습니다. 따라서이 결정은 전체 서버에 영향을 미칩니다.

ASP.NET에서 병렬 코드를 사용하면 웹 응용 프로그램의 확장 성을 실제로 제한하려고 결정하게됩니다. 특히 요청이 폭발적으로 발생하는 경우에는 상당한 양의 스레드 변동이있을 수 있습니다. 을 알고있는 경우 동시 사용자 수가 매우 낮을 때 (예 : 공용 서버가 아닌 경우) ASP.NET에서 병렬 코드 만 사용하는 것이 좋습니다.

이렇게 멀리까지 오면 ASP.NET에서 병렬 처리를하고 싶다면 몇 가지 옵션이 있습니다.

쉬운 방법 중 하나는 기존 코드와 매우 유사한 Task.Run을 사용하는 것입니다. 그러나 CalculateAsync 메서드를 구현하지 않는 것이 좋습니다. 비동기식 처리이므로 처리되지 않습니다. 대신, 호출의 시점에서 Task.Run을 사용 : 그것은 당신의 코드에서 잘 동작하는 지 여부를

private async Task MakeRequest() 
{ 
    // do some stuff 
    var task = Task.Run(() => Calculate(myInput)); 
    // do other stuff 
    var myOutput = await task; 
    // some more stuff 
} 

또는, 당신은 Parallel 유형, 즉, Parallel.For, Parallel.ForEach, 또는 Parallel.Invoke를 사용할 수 있습니다.

private void MakeRequest() 
{ 
    Parallel.Invoke(() => Calculate(myInput1), 
    () => Calculate(myInput2), 
    () => Calculate(myInput3)); 
} 

I 권장하지 않는다 : 스레드 컨텍스트에서 실행 재개 Parallel 코드 장점은 그 요청 스레드 병렬 스레드 중 하나로서 사용되는, 그리고합니다 (async 예 이하 문맥 전환있다) ASP.NET에서 병렬 LINQ (PLINQ)를 사용합니다.

+0

자세한 답변을 주셔서 감사합니다. 내가 분명히하고 싶은 한 가지 더. 'Task.Run'을 사용하여 실행을 시작하면 내용은 다른 스레드에서 실행됩니다. 스레드 풀에서 가져옵니다. 그렇다면 블로킹 호출 (예 : 서비스 호출)을'Run'으로 랩 할 필요가 없습니다. 왜냐하면 항상 메소드를 실행하는 동안 블로킹 될 하나의 쓰레드를 소비하기 때문입니다. 그러한 상황에서 나의 경우에는 'async-await'에서 남겨진 유일한 이익은 한 번에 여러 가지 작업을 수행하는 것입니다. 내가 틀렸다면 나를 바로 잡아주세요. – Eadel

+0

또한 "서비스"는 원격 웹 서비스에 대한 호출입니다.이 서비스는 우리 서버에 CPU 바인딩되지 않지만 원격 시스템에서 응답을 얻으려면 시간이 필요합니다. 두 번째 가정에 대한 주제에서 대부분의 요청 중에 작업을 수행 할 수있는 옵션을 제공하지만이 경우에는 속도와 서버로드가 모두 중요합니다. 응용 프로그램은 내부 응용 프로그램이 아니므로 최대로드가 많은 요청이 발생할 수 있습니다. – Eadel

+0

네,'Run' (또는'Parallel')에서 얻을 수있는 유일한 이점은 동시성입니다. 작업은 여전히 ​​각각 스레드를 차단합니다.서비스가 웹 서비스라고 말했기 때문에'Run'이나'Parallel'을 사용하지 않는 것이 좋습니다. 대신 서비스 용 비동기 API를 작성하십시오. –

관련 문제