2014-04-10 2 views
0

루프를 통과하는 프로세스가 있습니다. 루프가 반복 될 때마다 외부 웹 서비스를 호출 한 다음 객체를 EntityFramework 저장소에 추가합니다. 외부 서비스 호출은 정적 메서드로 래핑됩니다. 일반적으로 루프에는 하나 또는 두 개의 반복 만 있지만 현재 UI에서는 최대 4 개까지 반복 할 수 있습니다. (각 반복은 보험 견적을 나타냄).비동기 프로세스를 호출하는 루프를 리 팩터 처리합니다.

비동기 프로세스로 리팩터링하는 것이 이점으로 보입니다. 각 반복이 별도의 스레드에서 발생하도록 모든 스레드를 완료 할 때까지 대기하도록 커늘을 설정하는 방법은 무엇입니까?

public class ProcessRequest 
{ 
    private IUnitOfWork = unitOfWork; 

    public ProcessRequest(IUnitOfWork uow) 
    { 
     unitOfWork = uow; 
    } 

    public void Execute(MyRequestParams p) 
    { 
     foreach (Quote q in p.Quotes) 
     { 
      q.Premium = QuoteService.GetQuote(q); 
      unitOfWork.GetRepository<Quote>().Add(q); 
     } 

     unitOfWork.Commit(); 
    } 
} 

public static class QuoteService 
{ 
    public static decimal GetQuote(Quote quote) 
    { 
     //I've simplified proprietary code to single line that calls an external service 
     return ExternalWebService.GetQuote(quote.Deductible); 
    } 
} 
+1

이 WPF, WinForms, Webforms, MVC, Web API입니까? (답변과 매우 밀접한 관련이 있습니다.) –

+0

이것은 n-tier 솔루션의 비즈니스 계층입니다. 비즈니스 계층은 MVC 프런트 엔드에 의해 호출됩니다. 비즈니스 계층을 UI가 호출하는 Restful API로 변환하는 것에 대한 논의가있었습니다. UI는 현재 트래픽이 적습니다 (소수의 사용자가있는 인트라넷). 그러나 먼 미래가 아니라면 확장 될 수 있습니다. –

답변

0

당신은 다른 두 가지를 요구하고 하나는 각각의 반복이 발생할 경우 별도의 스레드에서 (잠재적으로), 병렬에 루프 을 실행하는 방법입니다; 이는 루프 전체를 비동기 프로세스으로 실행하는 것과 완전히 다릅니다. 즉, 시작하는 스레드가 완료 될 때까지 기다리지 않음을 의미합니다. 첫 번째를 의미한다고 가정합니다. 즉 루프의 반복을 병렬 처리하려고하지만 모든 작업이 완료 될 때까지 계속 차단하고 싶다고 가정합니다.

public void Execute(MyRequestParams p) 
{ 
    Parallel.ForEach(p.Quotes, q => { 
    q.Premium = QuoteService.GetQuote(q); 
    unitOfWork.GetRepository<Quote>().Add(q); 
    }); 

    unitOfWork.Commit(); 
} 

아니면 뭔가 같은 :

public void Execute(MyRequestParams p) 
{ 
    Parallel.ForEach(p.Quotes, q => { 
    q.Premium = QuoteService.GetQuote(q); 
    }); 

    unitOfWork.GetRepository<Quote>().AddAll(p.Quotes); 
    unitOfWork.Commit(); 
} 

이것은 따라이 실행되는 상황에 대해 아무것도 모른 채

, 하나의 간단한 방법은 병렬 확장, 특히 병렬의 Foreach를 사용하는 것 당신이 다루는 스레드 안전에 크게 의존합니다.

+0

네 말이 맞아. 나는 그 질문을 분명하게 묻지 않았다. 각 반복은 비동기 적이어야하며 다음 반복을 시작하기 전에 한 번의 반복이 중지 될 때까지 기다리지 않아야한다고 말하려고합니다. 너 내가 그랬을 때 더 잘 말 했어. 귀하의 회신에 감사 드리며 귀하의 솔루션을 좋아합니다. –

0

대부분의 작업이 I/O 인 경우 서비스/DB가 응답 할 때까지 기다리는 시간을 대부분 낭비하므로 새 스레드를 돌리는 데 대한 확신이 없습니다.

나는 비동기 방식으로 이동하려고 할 것 :이 방법으로

public async Task Execute(MyRequestParams p) 
{ 
    foreach (var quote in p.Quotes) 
    { 
    //Of course, you'll need an async endpoint. 
    var q.Premium = await QuoteService.GetQuoteAsync(q); 
    } 

    unitOfWork.GetRepository<Quote>().AddAll(p.Quotes); 
    await unitOfWork.SaveChangesAsync(); 
} 

, 새로운 스레드를 회전하고 대부분의 시간을 유휴시키는 오버 헤드를 저장합니다. 희망적이라면 당연히 웹 서비스의 비동기 끝점에 대한 액세스 권한을 갖고 Entity Framework v6을 사용해야합니다.

+0

나는이 해결책을 좋아한다. Parellel.ForEach와 Task/Await의 차이점을 읽어야합니다. 나는 웹 서비스를위한 비동기 엔도 핀트를 가지고 있는지 확인하겠습니다. 다시 한번 감사드립니다. –

+1

기본적으로 async/await를 사용하면 순차적으로 루프를 실행하지만 각 _await_ed 부분은 별도의 스레드에서 호출 할 수 있으므로 원래 스레드에서 걱정할 필요가 없습니다 (원래 스레드 웹 서버 작업자 스레드 인 경우 대기 상태로 유지하지 않으려는 경우). Parallel.ForEach 솔루션은 다른 스레드가 동시에 (순차적으로) 작업을 수행하기를 기다리는 동안 원래 작업자 스레드를 보유합니다. 그들은 정말로 다른 문제를 해결하고 있습니다 (필요한 경우 결합 할 수 있습니다). – Avish

+0

Async/Await을 사용하면 동기식 방식으로 I/O 기반 비동기 작업을 작성할 수 있습니다. 유선을 통해 HTTP 메시지를 보내려고합니다. HTTP 메시지를 생성하여 웹 서비스로 전송하면 해당 지점의 스레드가 웹 서비스가 응답을 검색 할 때까지 대기합니다. 비동기를 사용하면 요청을 듣고 "듣고, 지금은 발신자에게 돌아가지만, 끝나면 알려주고 아플 때부터 중단했던 지점으로 다시 뛰어 오십시오"라고 말할 수있었습니다. 따라서 기본적으로 I/O 작업을 차단하는 작업의 스레드 리소스를 허리띠로 볼 수 없습니다. –

관련 문제