2013-10-13 2 views
2

한 번에 3 개씩 항목을 처리해야하는 총계 시간이 x 초 미만인 경우 스레드는 나머지 초 동안 잠자기를 기다렸다가 계속 진행해야합니다. 동적 지연을 사용하는 C# 작업

그래서 나는 다음과 같은 일을 해요 :

private void ProcessItems() 
    { 
     for (int i = 0, n = items.Count; i < n; i++) 
     { 
      Stopwatch stopwatch = new Stopwatch(); 
      stopwatch.Start(); 

      batch.Add(items[i]); 

      if (batch.Count == 3 || i >= items.Count - 3) 
      { 
       List<Task> tasks = new List<Task>(3); 

       foreach (Item item in batch) 
        tasks.Add(Task.Factory.StartNew(() => ProcessItem(item))); 

       Task.WaitAll(tasks.ToArray()); 

       batch.Clear(); 
      } 

      stopwatch.Stop(); 

      int elapsed = (int)stopwatch.ElapsedMilliseconds; 
      int delay = (3000) - elapsed; 

      if (delay > 0) 
       Thread.Sleep(delay); 
     } 
    } 

ProcessItem 함수는 WebRequest 클래스를 만들고 응답 (콜백)을 처리합니다. 이것은 약간의 시간이 걸리는 기능입니다.

그러나 작업을 올바르게 이해하면 스레드가 여러 작업을 수행 할 수 있습니다. 따라서 스레드를 잠그면 다른 작업이 영향을받을 수 있습니다.

위를 달성하는보다 효율적인 방법이 있으며 Parallel.Foreach에서 작업을 사용할 수 있습니까? 사용

Task.Delay 

+0

전체 소스 코드가 포함 된 최종 해결책이 있습니까? – Kiquenet

답변

2

작업은 자동 관리 스레드에서 실행됩니다. 스레드를 차단할 때 본질적으로 잘못된 점은 없습니다. 조금 낭비입니다.

MyItem[] items = ...; 
foreach(MyItem[] itemsChunk in items.AsChunked(3)) { 
Parallel.ForEach(itemsChunk, item => Process(item)); 
//here you can insert a delay 
} 

이 폐기물이 아닌 단일 스레드를하고 하찮게 간단하다 : 여기

내가 아주 깔끔하게이를 구현하는 것이 방법이다. Parallel.ForEach은 현재 스레드를 사용하여 작업 항목을 처리하므로 유휴 상태가 아닙니다. 지연 논리를 추가 할 수도 있습니다. 구현은 AsChunked 독자를위한 연습으로 남아 있습니다 ...이 함수는 지정된 크기 (3)의 청크로 목록을 분할하기로되어 있습니다. 이러한 도우미 기능에 대한 좋은 점은 중요한 부분의 일괄 처리 로직을 풀다는 것입니다.

귀하의 ProcessItems 방법은 아주 쉽게 비동기 버전 ProcessItemsAsync (필자는 "배치"논리를 확인하지 않은)로 변환 될 수
+0

지연은 Thread.Sleep()으로 구현되어 있습니까? –

+1

예. 스레드를 구울 싶지 않으면'await Task.Delay'를 사용하십시오. 하지만 전체 비동기 모델을 강요합니다. 작업을 사용하려면 모든 발신자를 변경해야합니다. 스레드가 거의 없다면 스레드가 일정한 오버 헤드를 가지고 있기 때문에 어떤 모델을 선택했는지 perf 관점에서 중요하지 않습니다. 비동기에 비해 실행 속도가 느리거나 빨라지지는 않습니다. 물건을 복잡하게하기 때문에 필요하지 않다면 async를 사용하지 말 것을 권장합니다. – usr

+0

어떻게하면 Task.Dwaay 귀하의 예제에 맞게 기다리고 있습니까? –

0

대신

static async Task DoSomeProcess() 
    { 
     await Task.Delay(3000); 
    } 

자네 말이 맞아

는 Thread.sleep를 다른 작업

예를 병렬로 비동기/await를 패턴을 페어링 할 수

을 차단하는 것입니다.

0

:

private async Task ProcessItemsAsync() 
{ 
    for (int i = 0, n = items.Count; i < n; i++) 
    { 
     Stopwatch stopwatch = new Stopwatch(); 
     stopwatch.Start(); 

     batch.Add(items[i]); 

     if (batch.Count == 3 || i >= items.Count - 3) 
     { 
      List<Task> tasks = new List<Task>(3); 

      foreach (Item item in batch) 
       tasks.Add(Task.Run(() => ProcessItem(item))); 

      await Task.WhenAll(tasks.ToArray()); 

      batch.Clear(); 
     } 

     stopwatch.Stop(); 

     int elapsed = (int)stopwatch.ElapsedMilliseconds; 
     int delay = (3000) - elapsed; 

     if (delay > 0) 
      await Task.Delay(delay); 
    } 
} 

유일한 이점은 당신이 ProcessItems 스레드를 차단하지 않는 것 그의 대답에서 지적한 바와 같이 Task.WaitAll()Thread.Sleep()이 있습니다. 이 방법을 사용할지 아니면 Parallel.ForEach을 사용할지 여부는 아마도 코드의 실행 환경에 따라 달라집니다. Async/await은 코드 실행 속도를 향상시키지 않지만 서버 측 실행에 대한 확장 성을 향상시킵니다. 실행되는 스레드가 줄어들어 더 많은 클라이언트가 서비스 될 수 있기 때문입니다.

참고 또한 지금 ProcessItemsAsync 그래서, 당신은 다음과 같이 호출 할 필요가 거라고 변화를 호출하는 코드의 흐름을 유지하기 위해 비동기 작업 자체가 : 블로킹 호출이

ProcessItemsAsync().Wait(); 

자체적으로 우리가 방금 얻은 async의 이점을 죽일 수 있습니다. 앱에서 이와 같은 블록을 완전히 제거 할 수 있는지 여부는 앱의 나머지 워크 플로에 따라 크게 달라집니다.

관련 문제