2017-09-26 14 views
0

한 번에 최대 2 개 작업을 병렬 처리하여 5 개의 작업을 완료해야합니다. 따라서 일부 작업이 완료 되 자마자 다음 작업은 보류중인 작업이 없을 때까지 실행해야합니다.Task.Factory.StartNew의 임의 작업이 시작되지 않음

나는 작업간에 동기화를 위해 세마포어를 사용하는 solution by L.B.을 사용하고 있습니다.

void LaunchTaskPool() 
    { 
     SemaphoreSlim maxThreadSemaphore = new SemaphoreSlim(2); //Max 2 tasks at a time. 

     for (int i = 0; i < 5; i++)      //loop through 5 tasks to be assigned 
     { 
      maxThreadSemaphore.Wait();     //Wait for the queue 

      Console.WriteLine("Assigning work {0} ", i); 

      Task t = Task.Factory.StartNew(() => 
      { 
       DoWork(i.ToString());     // assign tasks 
      }, TaskCreationOptions.LongRunning 
       ) 
       .ContinueWith(
       (task) => maxThreadSemaphore.Release() // step out of the queue 
       ); 
     } 

    } 

    void DoWork(string workname) 
    { 
     Thread.Sleep(100); 
     Console.WriteLine("--work {0} starts", workname); 
     Thread.Sleep(1000); 
     Console.WriteLine("--work {0} finishes", workname); 

    } 

문제는 임의의 작업이 시작되지 않는다는 것입니다.

Output

내가 제안 here로 Task.WaitAll()를 추가했지만, 그것은 도움이되지 않았다 예를 들어 여기에 1, 시작되지 및 워크 4는 두 번 실행있어 결코 3 작업 할 수 있습니다.

미리 감사드립니다.

콘스탄틴.

+0

https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop을 -variable-considered-harmful/ –

답변

5

이 대신 Parallel.For()을 사용하는 것이 좋습니다. 바퀴를 재발 명할 필요가 없습니다! 예를 들어

: Parallel.For()를 사용하는 경우가 MaxDegreeOfParallelism를 지정할 수 있습니다

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApp4 
{ 
    class Program 
    { 
     static void Main() 
     { 
      Parallel.For(
       0, // Inclusive start 
       5, // Exclusive end 
       new ParallelOptions{MaxDegreeOfParallelism = 2}, 
       i => DoWork(i.ToString())); 
     } 

     static void DoWork(string workname) 
     { 
      Thread.Sleep(100); 
      Console.WriteLine("--work {0} starts", workname); 
      Thread.Sleep(1000); 
      Console.WriteLine("--work {0} finishes", workname); 

     } 
    } 
} 

을 (사실 난 그냥보고, 이것은 당신이 연결된 스레드의 다른 답변 하나에 이미 - 당신이 didn를 이유가있다 'T는 그 솔루션을 사용하지 않으면, 우리가 중복으로이 질문을 닫아야합니다 같아요 ...)

을 어쨌든 실제 질문에 대답 :

You are accessing a "modified closure" in the loop.는이 문제를 해결하려면,의 복사본을 만들을 고리 변수 i 작업에 전달하기 전에 :

SemaphoreSlim maxThreadSemaphore = new SemaphoreSlim(2); //Max 2 tasks at a time. 

for (int i = 0; i < 5; i++)      //loop through 5 tasks to be assigned 
{ 
    maxThreadSemaphore.Wait();     //Wait for the queue 

    Console.WriteLine("Assigning work {0} ", i); 
    int copy = i; // <----- Make a copy here. 

    Task t = Task.Factory.StartNew(() => 
      { 
       DoWork(copy.ToString());     // assign tasks 
      }, TaskCreationOptions.LongRunning 
     ) 
     .ContinueWith(
      (task) => maxThreadSemaphore.Release() // step out of the queue 
     ); 
} 
+0

수정 된 클로저에 대한 솔루션 및 링크는 Matthew에게 감사드립니다. – BusinessAlchemist

1

문제를 솔루션으로 Task이 시작되기 전에 루프가 allready를 통해 실행하고 다음 Task를 시작한다는 것입니다.

@Matthew Watson이 추천 한대로 Parallel.For을 사용해야합니다.


이 문제 해결할 그냥 관심 밖으로 :

static void LaunchTaskPool() 
{ 
    SemaphoreSlim maxThreadSemaphore = new SemaphoreSlim(2); //Max 2 tasks at a time. 

    for (int i = 0; i < 5; i++)      //loop through 5 tasks to be assigned 
    { 
     maxThreadSemaphore.Wait();     //Wait for the queue 

     Console.WriteLine("Assigning work {0} ", i); 

     StartThead(i, maxThreadSemaphore); 
    } 
} 

static void StartThead(int i, SemaphoreSlim maxThreadSemaphore) 
{ 
    Task.Factory.StartNew(
     () => DoWork(i.ToString()), 
     TaskCreationOptions.None 
    ).ContinueWith((task) => maxThreadSemaphore.Release()); 
} 

static void DoWork(string workname) 
{ 
    Thread.Sleep(100); 
    Console.WriteLine("--work {0} starts", workname); 
    Thread.Sleep(1000); 
    Console.WriteLine("--work {0} finishes", workname); 
}