2017-09-26 128 views
0

我需要一次完成5個任務並行執行的最大2個任務。 因此,一旦某個任務完成,下一個應該運行,直到沒有待處理的任務。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); 

    } 

問題是,一些隨機任務甚至不會啓動。例如在這裏工作1和3從來沒有開始工作,並得到了4運行兩次:

Output

我嘗試添加Task.WaitAll()的建議here,但它並沒有幫助。

在此先感謝您的建議!

君士坦丁。

+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); 

     } 
    } 
} 

(其實我只是看着,而這已經是你鏈接的線程其他的答案之一 - 有你沒理由「不想使用該解決方案,如果沒有,我想我們應該關閉這個問題作爲一個重複...)

反正回答您的實際問題:?

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開始之前在循環媒體鏈接運行通過,並開始下一個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); 
}