在Task期间返回错误结果的方法



我有一个循环创建三个任务:

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,仍然会导致不好的结果。

解决这个问题的一种方法是将循环变量复制到一个局部变量中,并且只在lambda中使用该局部变量:
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选项限制工作任务的数量。这是一个阻塞调用,因为它使用当前线程来处理数据以及任何工作任务。

相关内容

  • 没有找到相关文章

最新更新