[英]Method giving back wrong result during 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;
}
}
但是每個任務都會得到完全相同的字符串。
在 //CHECK-Point1 上,我檢查 Dic 的更改值:Correct Value for each Task
在 //CHECK-Point2 上,我檢查了字符串:所有 3 個任務上的相同字符串(當然應該不同)
默認詞典看起來像這樣:(縮短)
{
{"[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
捕獲循環變量device
,因此當執行任務時,所有任務都將使用該變量的內容。 如這個 SO 問題所示,即使沒有任務也會出現同樣的問題。 以下代碼將打印 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
如果問題的代碼使用了一個沒有Task.Run
的Action
,它仍然會導致不好的結果。
解決此問題的一種方法是將循環變量復制到局部變量中,並僅在 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
選項限制工作任務的數量。 這是一個阻塞調用,因為它使用當前線程來處理數據以及任何輔助任務。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.