none
Why my program have race condition? RRS feed

  • Question

  • Hi,

    I do not understand why I get race condition on this program, for me it seems that there is no problem, I don't understand why the threads mix the data, because for me it seems like I am creating new instances for each thread.

    What am I doing wrong? How can I solve this proble without using locks (I cant use locks because that would slow down the program that is more complex that the one shown here)?

    Thanks in advance I attach the code below.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Data;
    using System.IO;
    
    namespace NoSubClass
    {
        class Program
        {
            //inputs
            private static string path = @Q:\FV\MOD-03 (Shadings Simulation)\FASE 2 (RayTracing)\01 - Modelo\postes\nuevoformato.txt;
            private static string resultsPath = @Q:\FV\MOD-03 (Shadings Simulation)\FASE 2 (RayTracing)\01 - Modelo\tracker_results\;
            private static double A = 4;
            private static double R = 10;
            private static double maxTilt = 55.0 * Math.PI / 180;
            private static int Cx = 100;
            private static int Cy = 100;
            private static int nThreads = 2;
            private static Tracker[] allTrackers = null;
            private static List<Tracker[]> TrackerGroups = null;
            private static int trackingStrategy = 0; //0-->ltrue tracking; 1-->backtracking
            static void Main(string[] args)
            {
                Program.Run();
                Console.ReadLine();
            }
            
                        
            public static async void Run()
            {
                //import 
                allTrackers = Import.ImportTrackers(path, A, R, maxTilt, Cx, Cy);
                TrackerGroups = Import.splitList(nThreads, allTrackers.ToList());
    
                Task[] tasks = new Task[TrackerGroups.Count()];
    
                //loop over group of trackers
                for (int g = 0; g < TrackerGroups.Count(); g++)
                {
    
                    // progressBar1.Maximum = TrackerGroups[0].Count();
                    int p = 0;
                    int G = g;
                    Tracker[] groupTrackers = TrackerGroups[G];
                    Tracker[] tempallTrackers = allTrackers;
                    double a = A;
                    double r = R;
                    double cx = Cx;
                    double cy = Cy;
                    double maxtilt = maxTilt;
                    int tracking = trackingStrategy;
                    tasks[G] = new Task(() =>
                    {
                        //loop over trackers on group
                        for (int t = 0; t < TrackerGroups[G].Count(); t++)
                        {
                            int temp = t;
                            //set parameters of tracker to study
                            Tracker tracker = TrackerGroups[G][temp];
                            Plant plt = new Plant();
                            plt.calculateShadows(allTrackers, tracker, temp, G);
    
                        }
                    });
    
                    tasks[G].Start();
                    //progressBar1.Value = g;
                    p++;
                }
    
    
                await Task.WhenAll(tasks);
                
            }
        
            
        }
    
        class Plant
        {
            private object _lck = new object();
            public void calculateShadows(Tracker[] allTrackers, Tracker tracker, int track, int group)
            {
                tracker.SetCornerCoordinates(0.56);
                tracker.SetTilt(0.56, 0.0345, 0);
    
    
                double shadow = 0;
                //random calculations
                for (int t = 0; t < allTrackers.Count(); t++)
                {
                    Tracker ftracker = allTrackers[t];
                    ftracker.SetCornerCoordinates(0.56);
       
                    shadow += 0.003 * tracker.getCornerCoordinates()[0, 0] * tracker.getCornerCoordinates()[1, 0] / (ftracker.getCornerCoordinates()[2, 1] * ftracker.getCornerCoordinates()[0, 2]);
    
                    
    
                }
    
                Console.WriteLine("thread  number " + Task.CurrentId + " shadow is: " + shadow + " tracker: " + track + " group: " + group);
            }
        }
    
        class Tracker
        {
            //initial data (all private and imported)
            private double[] northPost; //x,y,z coordinates of south post of the tracker
            private double[] southPost; //x,y,z coordinates of south post of the tracker
            private double A;  //tracker width
            private double R;  //tracker pitch
            private double maxTilt; //tilt limit
            private int Cx, Cy; //cells divisions
    
            //geometrical useful data
            private double[,] cornerCoordinates = new double[4, 3];
            private double[,,] trackerCells;
            private double tilt;
            private double[] normal;
    
            //------------Methods-----------------
    
            //Set initial data:
            public void SetInitialData(double[] northPost, double[] southPost, double A, double R, double maxTilt, int Cx, int Cy)
            {
                this.northPost = northPost;
                this.southPost = southPost;
                this.A = A;
                this.R = R;
                this.maxTilt = maxTilt;
                this.Cx = Cx;
                this.Cy = Cy;
                this.trackerCells = new double[Cx, Cy, 3];
                //coordenadas de celdas
                //0, 1, 2 - x, y, z
    
            }
    
            //Set tilt
            public void SetTilt(double az, double ele, int tracking)
            {
                //tracking parameters
                double sun3d_z;
                double sun3d_x;
                double angle3;
                double eleSun2d;
                double eleSun2d_treshold = Math.Asin(A / R);
    
                sun3d_x = -Math.Cos(ele) * Math.Sin(az);
                sun3d_z = Math.Sin(ele);
                eleSun2d = Math.Atan2(sun3d_z, Math.Abs(sun3d_x));
    
                if (tracking == 1 && eleSun2d < eleSun2d_treshold)
                {//backtracking
                    angle3 = Math.PI - Math.Asin((R / A) * Math.Sin(eleSun2d));
                    tilt = Math.Abs(Math.PI - eleSun2d - angle3);
                }
                else
                {//truetracking
                    if (ele > 0)
                    {
                        tilt = Math.Abs(Math.Atan(Math.Cos(az - Math.PI * 0.5) / Math.Tan(ele)));
                    }
                    else
                    {
                        tilt = maxTilt;
                    }
                    if (tilt > maxTilt)
                    {
                        tilt = maxTilt;
                    }
                }
            }
    
            //Set corner coordinates
            public void SetCornerCoordinates(double az)
            {
                //post coordinates
                double xN = northPost[0];
                double yN = northPost[1];
                double zN = northPost[2];
    
                double xS = southPost[0];
                double yS = southPost[1];
                double zS = southPost[2];
    
                //highest north corner[0,]
                cornerCoordinates[0, 0] = xN;//x
                if (az > 0)
                {//sun on the east
                    cornerCoordinates[0, 1] = yN - A * 0.5 * Math.Cos(tilt);//y
                }
                else
                {//sun on the west
                    cornerCoordinates[0, 1] = yN + A * 0.5 * Math.Cos(tilt);//y
                }
                cornerCoordinates[0, 2] = zN + A * 0.5 * Math.Sin(tilt); //z
    
                //highest south corner[1,]
                cornerCoordinates[1, 0] = xS;//x
                if (az > 0)
                {//sun on the east
                    cornerCoordinates[1, 1] = yS - A * 0.5 * Math.Cos(tilt);//y
                }
                else
                {//sun on the west
                    cornerCoordinates[1, 1] = yS + A * 0.5 * Math.Cos(tilt);//y
                }
                cornerCoordinates[1, 2] = zS + A * 0.5 * Math.Sin(tilt); //z
    
                //lowest  north corner[2,]
                cornerCoordinates[2, 0] = xN;//x
                if (az > 0)
                {//sun on the east
                    cornerCoordinates[2, 1] = yN + A * 0.5 * Math.Cos(tilt);//y
                }
                else
                {//sun on the west
                    cornerCoordinates[2, 1] = yN - A * 0.5 * Math.Cos(tilt);//y
                }
                cornerCoordinates[2, 2] = zN - A * 0.5 * Math.Sin(tilt);
    
                //lowest south corner[3,]
                cornerCoordinates[3, 0] = xS;//x
                if (az > 0)
                {//sun on the east
                    cornerCoordinates[3, 1] = yS + A * 0.5 * Math.Cos(tilt);//y
                }
                else
                {//sun on the west
                    cornerCoordinates[3, 1] = yS - A * 0.5 * Math.Cos(tilt);//y
                }
                cornerCoordinates[3, 2] = zS - A * 0.5 * Math.Sin(tilt); //z        
            }
    
            //getCorner coordinates
            public double[,] getCornerCoordinates()
            {
                return cornerCoordinates;
            }
        }
    
        class Import
        {
            public static Tracker[] ImportTrackers(string path, double A, double R, double maxTilt, int Cx, int Cy)
            {
                //import data of trackers
                List<Tracker> listofTrackers = new List<Tracker>();
                List<string> data = ConvertCSVtoDataTable(path);
                bool nextTracker = true;
                Tracker tracker = null;
                double[] nPost = null;
                double[] sPost = null;
    
                for (int i = 0; i < data.Count; i++)
                {
    
                    //save south and north post, x=y, y=x
                    data[i] = data[i].Replace(",", ".");//replace commas for point
    
                    if (nextTracker)
                    {
                        nPost = new double[3];
                        sPost = new double[3];
                        tracker = new Tracker();
                    }
    
                    if (data[i].Split(';')[0] == "VN")
                    {
                        nPost[0] = Convert.ToDouble(data[i].Split(';')[2]);
                        nPost[1] = Convert.ToDouble(data[i].Split(';')[1]);
                        nPost[2] = Convert.ToDouble(data[i].Split(';')[5]);
    
                        nextTracker = false;
                    }
    
                    if (data[i].Split(';')[0] == "VS")
                    {
                        sPost[0] = Convert.ToDouble(data[i].Split(';')[2]);
                        sPost[1] = Convert.ToDouble(data[i].Split(';')[1]);
                        sPost[2] = Convert.ToDouble(data[i].Split(';')[5]);
    
                        tracker.SetInitialData(nPost, sPost, A, R, maxTilt, Cx, Cy);
                        listofTrackers.Add(tracker);
                        nextTracker = true;
                    }
    
                }
    
                return listofTrackers.ToArray();
            }
    
            //import .csv function
            public static List<string> ConvertCSVtoDataTable(string strFilePath)
            {
                using (var reader = new StreamReader(strFilePath))
                {
                    List<string> listA = new List<string>();
                    while (!reader.EndOfStream)
                    {
                        var line = reader.ReadLine();
                        var values = line;
    
                        listA.Add(values);
    
                    }
                    return listA;
                }
            }
    
            public static List<Tracker[]> splitList(int nsplits, List<Tracker> ArrayTotal)
            {
                List<Tracker[]> splittedLists = new List<Tracker[]>();
    
                int step = ArrayTotal.Count() / nsplits;
    
    
                for (int i = 0; i < ArrayTotal.Count(); i += step)
                {
                    if (i + step > ArrayTotal.Count())
                    {
                        Tracker[] subArray = (ArrayTotal.GetRange(i, ArrayTotal.Count() - i)).ToArray();
                        splittedLists.Add(subArray);
                    }
                    else
                    {
                        Tracker[] subArray = (ArrayTotal.GetRange(i, step)).ToArray();
                        splittedLists.Add(subArray);
                    }
    
                }
    
                return splittedLists;
            }
        }
    }
    

    Wednesday, August 14, 2019 12:59 PM

Answers

  • Hi all, finally I solved it. The problem was that I was using the class Tracker outside of the threads, and so its data was shared. I edited the code to only use class Tracker inside of the threads, and the program runs perfectly.

    Thanks all.

    • Marked as answer by Javier Waldo Tuesday, August 20, 2019 6:57 AM
    Monday, August 19, 2019 8:30 AM

All replies

  • Hi,

    I have a hard time understanding the exact logic of your code, and I can't run it without the input text file, so I'm going to give you a tip.

    When using Tasks, you could use Task<T> where T is a class that can contain your resulting data.

    If you can split up your problem in chunks (I guess you are doing that with the TrackerGroups) you can run the tasks completely separate. Create a method that gets the input for a chunk and returns the output for that chunk. Wait for all chunks to stop processing and then combine all results in the end.

    I have created an example using prime number calculation:

    class Program
    {
        static async Task Main(string[] args)
        {
            List<Task<Output>> processes = new List<Task<Output>>();
    
            for (int i = 0; i < 8; i++)
            {
                Input input = new Input { From = i * 100000, To = (i + 1) * 100000 };
                processes.Add(CountPrimeNumbers(input));
            }
    
            await Task.WhenAll(processes);
    
            int numberOfPrimeNumbers = processes.Sum(x => x.Result.NumberOfPrimeNumbers);
            Console.WriteLine(numberOfPrimeNumbers);
    
            Console.ReadKey();
        }
    
        static Task<Output> CountPrimeNumbers(Input input)
        {
            return Task.Run(() =>
            {
                int numberOfPrimeNumbers = 0;
    
                for (int number = input.From; number < input.To; number++)
                {
                    bool isPrime = true;
    
                    for (int divider = 2; divider <= Math.Sqrt(number); divider++)
                    {
                        if (number % divider == 0)
                        {
                            isPrime = false;
                        }
                    }
    
                    if (isPrime)
                    {
                        numberOfPrimeNumbers++;
                    }
                }
    
                return new Output { NumberOfPrimeNumbers = numberOfPrimeNumbers };
            });
        }
    }
    
    class Input
    {
        public int From { get; set; }
        public int To { get; set; }
    }
    
    class Output
    {
        public int NumberOfPrimeNumbers { get; set; }
    }

    Kind regards,

    Johnny Hooyberghs

    Wednesday, August 14, 2019 1:26 PM
  • Hi Javier Waldo,

    Thank you for posting here.

    I make a test on my side based on your description.

    Here's my code:

            private static int a = 1, b = 0;
            static void Main(string[] args)
            {
                Run(0);
                while (a<b)
                {
                    Run(a);
                    a++;
                }
                Console.ReadLine();
            }
            public static async void Run(int num)
            {
                allTrackers = Import.ImportTrackers(path, A, R, maxTilt, Cx, Cy);
                TrackerGroups = Import.splitList(nThreads, allTrackers.ToList());
    
                b = TrackerGroups.Count();
    
                Tracker[] groupTrackers = TrackerGroups[num];
                Tracker[] tempallTrackers = allTrackers;
                double a = A;
                double r = R;
                double cx = Cx;
                double cy = Cy;
                double maxtilt = maxTilt;
                int tracking = trackingStrategy;
                await Task.Run(()=> {
                    for (int t = 0; t < TrackerGroups[num].Count(); t++)
                    {
                        int temp = t;
                        Tracker tracker = TrackerGroups[num][temp];
                        Plant plt = new Plant();
                        plt.calculateShadows(allTrackers, tracker, temp, num);
                    }
                });
            }
        }

    I hope it can help you.

    Besides, if I have any misunderstanding, please provide some information about your text file, and more details about your problem.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, August 16, 2019 9:57 AM
    Moderator
  • How is the data being mixed?  The only way I can see that here is that the Console.WriteLine messages might be interweaved, but that's irrelevant to your computation.  If you need the Console messages to be atomic, then you will have to use a lock around the call to WriteLine.  There is no other way.

    Tim Roberts | Driver MVP Emeritus | Providenza &amp; Boekelheide, Inc.

    Friday, August 16, 2019 11:03 PM
  • Check that calling SetCornerCoordinates and getCornerCoordinates from multiple threads is a possible race condition.

    Maybe you should remove the calls of SetCornerCoordinates and execute it and also SetTilt using Parallel.ForEach, for example, before starting the tasks.


    • Edited by Viorel_MVP Saturday, August 17, 2019 8:54 AM
    Saturday, August 17, 2019 8:53 AM
  • Hi all, finally I solved it. The problem was that I was using the class Tracker outside of the threads, and so its data was shared. I edited the code to only use class Tracker inside of the threads, and the program runs perfectly.

    Thanks all.

    • Marked as answer by Javier Waldo Tuesday, August 20, 2019 6:57 AM
    Monday, August 19, 2019 8:30 AM
  • Hi Javier Waldo,

    I'm glad to hear that you have solved the problem, and you can post "Mark as answer" to the answer which is helpful to you, so that it will help other members to find the solution quickly if they face a similar issue.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, August 20, 2019 2:34 AM
    Moderator