我有一个循环创建三个任务:
List<Task> tasks = new List<Task>();
foreach (DSDevice device in validdevices)
{
var task = Task.Run(() =>
{
var conf = PrepareModasConfig(device, alternativconfig));
//CHECK-Point1
string config = ModasDicToConfig(conf);
//CHECK-Point2
if (config != null)
{
//Do Stuff
}
else
{
//Do other Stuff
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
它调用这个方法,其中默认配置的字典的一些数据被覆盖:
private Dictionary<string, Dictionary<string, string>> PrepareModasConfig(DSDevice device, string alternativeconfig)
{
try
{
Dictionary<string, Dictionary<string, string>> config = new Dictionary<string, Dictionary<string, string>>(Project.project.ModasConfig.Config);
if (config.ContainsKey("[Main]"))
{
if (config["[Main]"].ContainsKey("DevName"))
{
config["[Main]"]["DevName"] = device.ID;
}
}
return config;
}
catch
{
return null;
}
}
之后,它通过这个方法被转换成字符串:
private string ModasDicToConfig(Dictionary<string, Dictionary<string, string>> dic)
{
string s = string.Empty;
try
{
foreach (string key in dic.Keys)
{
s = s + key + "n";
foreach (string k in dic[key].Keys)
{
s = s + k + "=" + dic[key][k] + "n";
}
s = s + "n";
}
return s;
}
catch
{
return null;
}
}
但是每个task返回的是完全相同的字符串。
On//check - point1我检查Dic的变化值:每个任务的正确值
在//check - point2上检查字符串:所有3个任务上的相同字符串(当然应该不同)
Default-Dictionary看起来像这样:(缩短)
{
{"[Main]",
{"DevName", "Default"},
...
},
...
}
结果字符串看起来像这样:
[Main]
DevName=003 <--This should be different (from Device.ID)
...
[...]
编辑:我将这些方法移到任务外部执行。现在我得到了正确的结果。所以我猜这和任务有关?
List<Task> tasks = new List<Task>();
foreach (DSDevice device in validdevices)
{
var conf = PrepareModasConfig(device, alternativconfig));
//CHECK-Point1
string config = ModasDicToConfig(conf);
//CHECK-Point2
var task = Task.Run(() =>
{
if (config != null)
{
//Do Stuff
}
else
{
//Do other Stuff
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
问题不是由任务引起的。传递给Task.Run
的lambda捕获循环变量device
,因此当执行任务时,所有任务都将使用该变量的内容。即使没有任务,同样的问题也会发生,正如这个问题所示。下面的代码将打印10次:
List<Action> actions = new List<Action>();
for (int i = 0; i < 10; ++i )
actions.Add(()=>Console.WriteLine(i));
foreach (Action a in actions)
a();
------
10
10
10
10
10
10
10
10
10
10
如果题目的代码使用Action
而不使用Task.Run
,仍然会导致不好的结果。
for (int i = 0; i < 10; ++i )
{
var ii=i;
actions.Add(()=>Console.WriteLine(ii));
}
问题的代码可以通过将device
循环变量复制到循环中来修复:
foreach (DSDevice dev in validdevices)
{
var device=dev;
var task = Task.Run(() =>
{
var conf = PrepareModasConfig(device, alternativconfig));
另一种方法是使用Parallel.ForEach
并行处理所有项,使用所有可用的内核,而不显式地创建任务:
Parallel.ForEach(validdevices,device=>{
var conf = PrepareModasConfig(device, alternativconfig));
string config = ModasDicToConfig(conf);
...
});
Parallel.ForEach
允许通过MaxDegreeOfParallelism
选项限制工作任务的数量。这是一个阻塞调用,因为它使用当前线程来处理数据以及任何工作任务。