2017-09-26 127 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); 
}