我按照MSDN上的示例制作了自己的演示,用于创建限制并发性的任务调度程序。 maxDegreeOfParallelism设置为2.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TaskSchedulerThatLimitsConcurrency
{
class Program
{
static void Main(string[] args)
{
// Create a scheduler that uses two threads.
LimitedConcurrencyLevelTaskScheduler lcts = new LimitedConcurrencyLevelTaskScheduler(2);
List<Task> tasks = new List<Task>();
// Create a TaskFactory and pass it our custom scheduler.
TaskFactory factory = new TaskFactory(lcts);
CancellationTokenSource cts = new CancellationTokenSource();
// Use our factory to run a set of tasks.
Object lockObj = new Object();
for (int i = 0; i < 4; i++)
{
var myFactory = new TaskFactory(lcts);
Task t = myFactory.StartNew(() =>
{
if (cts.IsCancellationRequested)
return;
lock (lockObj)
{
MakeTest(i, 1);
}
}, cts.Token);
tasks.Add(t);
}
for (int i = 0; i < 4; i++)
{
var myFactory = new TaskFactory(lcts);
Task t1 = myFactory.StartNew(() =>
{
if (cts.IsCancellationRequested)
return;
lock (lockObj)
{
MakeTest(i, 2);
}
}, cts.Token);
tasks.Add(t1);
}
// Wait for the tasks to complete before displaying a completion message.
Task.WaitAll(tasks.ToArray());
Console.WriteLine("\n\nSuccessful completion.");
Console.Read();
}
private static void MakeTest(int i, int p)
{
StringBuilder sb = new StringBuilder();
sb.Append(i.ToString() + "_" + p.ToString());
Console.WriteLine(sb.ToString());
}
}
}
结果是
任务t1和t2几乎相同。区别在于MakeTest(i, 1)
和MakeTest(i, 2)
。我想知道我可以使用第二个for循环来添加任务吗?
我使用了以下代码,但显然结果是错误的。
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < 4; i++)
{
var myFactory = new TaskFactory(lcts);
Task t1 = myFactory.StartNew(() =>
{
if (cts.IsCancellationRequested)
return;
lock (lockObj)
{
MakeTest(i, j+1);
}
}, cts.Token);
tasks.Add(t1);
}
}
结果:
问题:
答案 0 :(得分:2)
您传递给StartNew
的lambda会在变量i
和j
上创建一个闭包。由于变量是关闭的,而不是该变量的值,因此MakeTest()
将传递i
的值调用MakeTest()时{1}}和j
,而不是调用StartNew
时的值。假设您的for
循环在第一个线程到达代码中的该点之前完成,您将始终获得i
和j
具有的 last 值当他们通过for
循环时。
有关详细信息,请参阅Eric Lippert的文章:http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/
一个简单的解决方法是在for
循环内声明新变量。由于这些变量将在循环中作用域,因此捕获的值将保留变量在该特定迭代期间具有的值:
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < 4; i++)
{
var capturedI = i;
var capturedJ = j;
...
MakeTest(capturedI, capturedJ+1);
...