简体   繁体   中英

C# console application threading issue with Tasks

I have the following code snippets:

lock (lockObject) 
{
  foreach (KeyValuePair<string, List<string>> entry in productDictionary) 
  {
    string writePath = String.Format(@"{0}\{1}-{2}.txt", directoryPath, hour,entry.Key);
    Task writeFileTask = Task.Factory.StartNew(() => WriteProductFile(writePath, entry.Value));
  }
}

productDictionary is a ConcurrentDictionary of <string, List<string>> which I am trying to iterate over. For each key value pair I am attempting to construct a file path based on the Key and then write out the list of strings stored in the Value . For this purpose I start up a new task that calls the following method:

public static void WriteProductFile(string filePath, List<string> productQuotes)
{
    using(StreamWriter streamWriter = new StreamWriter(filePath)) 
    {
        productQuotes.ForEach(x => streamWriter.WriteLine(x));
    }
}

Stepping through the code everything looks fine at first. Putting a breakpoint at the method call for WriteProductFile shows that the correct parameters are being passed into the method through the Task. However, when my program actually makes it down to the WriteProductFile method the parameters have become mismatched. Ie, a List of strings that does not match the filepath has been passed in and so my data is no good once the program completes. No errors are thrown and the program executes fine but the wrong information is being written out to the wrong files.

I thought that the ConcurrentDictionary and Lock would take care of any threading issues that would arise but apparently I missed something. Any ideas?

You are capturing a loop variable. You must declare a local in inside the loop.

foreach (KeyValuePair<string, List<string>> entry in productDictionary) 
{
  string writePath = String.Format(@"{0}\{1}-{2}.txt", directoryPath, hour, entry.Key);
  List<string> list = entry.Value; // must add this.
  Task writeFileTask = Task.Factory.StartNew(() => WriteProductFile(writePath, list));
}

Prior to C# 5, a single loop variable is used for all iterations of the loop. Each closure references the same variable and therefore the value is updated when a new cycle of the loop starts. For a definitive explanation you should read Eric Lippert's post: Closing over the loop variable considered harmfuil.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM