-1

This is a follow up question to Use ThreadPool in C# within a loop and wait for all threads to finish and Save the content of several csv files into a searchable array C#

I have a code that looks like this:

using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;

    namespace ThreadPooling
    {
        class Program
        {
            static void Main(string[] args)
            {


                var FolderNames= new List<(string name)>()
                {
                  "folder1",
                  "folder2"
                }

                var tasks = new List<Task>();
                foreach (var folder in FolderNames)
                {
                    Console.WriteLine("Staring process" + folder.name + "...");
                    var task = Task.Run(() => Job(folder.name));
                    tasks.Add(task);
                }

                Task.WaitAll(tasks.ToArray());
                Console.WriteLine("All calculations done.");
                Console.WriteLine("\nPress any key to exit the program...");
                Console.ReadKey();

            }

            public class Job()
            {
               public Job(folder)
               {
                   CsvFile File = new CsvFile();
                   File.OpenCsvFiles(folder); //opens all csv files within the folder
               }
            }

    public class CsvFile
    {
        string folder;

        static Dictionary<string, Dictionary<int, Dictionary<int, string>>> _dictionary =
                                new Dictionary<string, Dictionary<int, Dictionary<int, string>>>();

        public void OpenCsvFiles(string folder) //opens all the csv files in the output folder and saves their's content to a Dictionary
        {
            this.folder = folder;

            foreach (string path in Directory.GetFiles(folder, "*.csv")) // fileName is the file name WITH its full path
            {
                try
                {
                    string[][] m_Data = File
                    .ReadLines(path)
                    .Where(line => !string.IsNullOrWhiteSpace(line))
                    //.Skip(1) //skipping the headers
                    .Select(line => line.Split(','))
                    .ToArray();

                    PopulateDictionary(Path.GetFileName(path), m_Data); //fills the Dictionary with each file
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error in OpenCsvFiles (" + path + ") : " + ex.Message);
                }
            }
        }

        static void PopulateDictionary(string filename, string[][] data)
        {
            _dictionary.Add(filename, new Dictionary<int, Dictionary<int, string>>());

            for (int i = 0; i < data.Length; i++)
            {
                _dictionary[filename].Add(i, new Dictionary<int, string>());

                for (int j = 0; j < data[i].Length; j++)
                {
                    _dictionary[filename][i].Add(j, data[i][j]);
                }
            }
        }



        }
    }
}

What I want to be able to do is to loop a list of folders, open all .csv files in each of the folders and save the values to a dictionary in CsvFile accessible to each Process that is run parallel. However, I run into the problem that C#, unlike C++, does not seem to create separate class objects of CsvFile within Process. That is, if I run into a file with the same file name in folder1 and folder2, say file Alpha.csv, I get an error that the code tires to add the same key, Alpha, twice to the dictionary.

But I want the dictionaries in CsvFile to be created and used separately with in each class object of Process, not one CsvFile object to be created and then used by all Process classes parallel. Is this possible?

KGB91
  • 630
  • 2
  • 6
  • 24

2 Answers2

0

This is because in .net reference types (object class, also string for example) are passed as reference, and value types (int, float, char etc) are passed as value.

If you want seperate objects you need to create a new object with the new operator. You can therefore do something like this:

var obj1 = new SomeClassIDefined(someInt, someString, someClass);
var obj2 = new SomeClassIDefined(obj.someInt, obj.someString, obj.someClass);

Additionally there's cloning, which basically is the same process above, but you move this to a seperate function instead. object already has a protected method you could use to clone an object:

https://learn.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=netframework-4.8

public class Person 
{
    public int Age;
    public string Name;
    public IdInfo IdInfo;

    public Person ShallowCopy()
    {
       return (Person) this.MemberwiseClone();
    }

    public Person DeepCopy()
    {
       Person other = (Person) this.MemberwiseClone();
       other.IdInfo = new IdInfo(IdInfo.IdNumber);
       other.Name = String.Copy(Name);
       return other;
    }
}

Be sure to carefully read though the docs of cloning! In short, you can clone items deep or shallow. Shallow means that you return a new object with all value types copied over, but all references to reference types stay intact. A deep copy means you copy value types, but also copy each reference type to a new object.

sommmen
  • 6,570
  • 2
  • 30
  • 51
  • I do have the line `CsvFile File = new CsvFile();` in my code above. But that does not seem to be enough? – KGB91 Apr 15 '20 at 08:54
  • `CsvFile File = new CsvFile();` i see that, but you're not really doing anything with the File variable – sommmen Apr 15 '20 at 08:56
  • Correct, I made an error in the code put here. But it still does not work. – KGB91 Apr 15 '20 at 11:31
0

The problem had to do with:

 static Dictionary

When I removed static it worked. Otherwise, _dictionary will be the same all during all runs.

KGB91
  • 630
  • 2
  • 6
  • 24