簡體   English   中英

在任務期間返回錯誤結果的方法

[英]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.RunAction ,它仍然會導致不好的結果。

解決此問題的一種方法是將循環變量復制到局部變量中,並僅在 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM