2014-12-03 2 views
0

ContinueWith가 작동하는 방법과 혼동 스럽지만 ThreadPool을 차단하고 작업을 순차적으로 실행합니다. 필자가 작성한 다음 코드 예제를 사용하십시오.ContinueWith가 순차적으로 작업을 차단 및/또는 실행하는 것 같습니다.

  var items = new List<int>(); 

     for (int i = 0; i < 100; i++) 
     { 
      items.Add(i); 
     } 

     Parallel.ForEach(items, item => 
      { 
       Task.Factory.StartNew(() => 
       { 

       }).ContinueWith(t => 
       { 
        if (item < 50) 
        { 
         System.Console.WriteLine("Blocking in {0}", item.ToString()); 
         var x = SomeLongRunningDatabaseCall(10001); 
        } 
        System.Console.WriteLine("Item {0}", item); 
       }); 
      }); 

코드 목적은 프로덕션 응용 프로그램에서 의심되는 스레드 차단 문제를 미러링하는 것이 었습니다. 위의 코드로 놀랍게도 ContinueWith를 사용하여 문제가있는 것으로 나타났습니다. 흥미로운 점은 ContinueWith에서 TaskContinuationOptions.LongRunning 옵션을 설정하면 차단하지 않고 작업을 비동기 적으로 실행한다는 것입니다. 또한 프로덕션 앱에서이 작업을 수행 했으므로 문제가 해결되었습니다.

그러나 난 정말 혼란스럽고, TaskContinuationOptions.LongRunning 옵션이없는 ContinueWith 문이 작업을 차단하거나 순차적으로 실행되는 것처럼 보이는 이유에 대해 더 잘 이해하고 싶습니다. 항목 반복마다 새 스레드를 호출하고 있습니다. . 내가 생각할 수있는 유일한 것은 ContinueWith가 선행 작업이 실행 된 스레드에서 실행되고 있지 않지만 블록을 일으킬 수있는 주 스레드에서 실행되고 있다는 것입니다.

도움이나 조언을 주시면 감사하겠습니다.

업데이트

또한 흥미로운 것은 내가

Thread.Sleep(600000) 

처럼 뭔가

SomeLongRunningDatabaseCall(10001) 

를 교체 할 경우 그것이 수행하는 데이터베이스에 있지만 이후 호출하지만 차단하지 않는다는 것입니다 그것은 자신의 스레드에서 실행 중이다. 적어도 50 이상의 타스크는 데이터베이스를 호출하지 않으므로 다른 타스크는 막히지 않아야한다.

+1

'ContinueWith'에 대한 호출이 거의 확실하게 차단되지 않습니다. 실제 작업은 순차적으로 실행될 수 있습니다. 그들이 존재한다면, 그것이 계속하고있는 것의 본성에 의한 것이지,'ContinueWith'의 구현 방법이 아닙니다. 그래서'Sleep' 호출이 병렬 처리되는 것을 볼 수 있습니다. – Servy

+0

@Servy 아, 무슨 뜻인지 알 겠어.다른 작업이 별도의 스레드에서 실행되고 있다고 생각할 때까지 작업> 50이 실행되지 않는다는 사실을 어떻게 설명 할 수 있습니까? (내 업데이트를 볼 수있는 기회가 있었는지 모르겠다.) –

+0

그저 일부 작업이 다른 작업을 기다리고있는 것 같습니다. 무한 병렬성을 가질 수는 없습니다. 필자는 병렬 DB 호출 수가 50 개를 초과 할 수 없다는 사실에 놀라지 않으며 병렬 실행에서 성능상의 이점도 기대할 수 없습니다. – Servy

답변

0

각 스레드는 자체 데이터베이스 연결 개체를 사용해야합니다. 내가 알고있는 대부분의 데이터베이스 연결은 논 - 로킹 멀티 - 쓰레드 모드를 지원하지 않는다. 이러한 데이터베이스 연결에서는 단일 연결이 병렬이 아닌 순차적으로 호출을 실행하기 때문에 스레드가 차단되는 것이 일반적이지 않습니다.

그런 경우 Parallel.ForEach()를 만들면 데이터베이스 연결이 상대적으로 비용이 많이 들기 때문에 앱이 제대로 작동하지 않을 수 있습니다. 실제로는 데이터베이스 연결을 한꺼번에 열 수는 없습니다 . item 개체를 ConcurrentQueue 또는 ConcurrentStack에 넣고이 컬렉션에 대해 3-5 개의 스레드 (또는 그 이상 - 여기서 실험해야 함)를 던져서 스택까지 실행할 수있게하는 등 다른 패턴을 사용하는 것이 더 나을 수 있습니다 또는 대기열이 고갈됩니다. 그렇게하면 인스턴스화 된 데이터베이스 연결 수를 안전한 숫자로 유지하면서도 불쾌한 차단을 피할 수 있습니다.

위의 내용은 Parallel.ForEach을 실제 데이터베이스 실행 (여기서는 LINQ-to-SQL 또는 EF에 대해서는 설명하지 않지만 실제 데이터베이스 연결 호출은 SqlCommand.Execute과 같은 것을 말함)을 사용하면 아마도 하위 수준이라고 생각합니다.

편집 : 내 제안을 위해 이전 구문을 사용할 필요가 없습니다. Tasks의 덩어리는 멋지게 할 것입니다.

관련 문제