none
How can I avoid using locks when using multithreads? RRS feed

  • Question

  • Hi,

    I have been trying for a while to implement multithreading in a program, but it always ends up mixing the data on results. The problem is that I can not use locks because that would slow down the program. How should I build my program in order to avoid sharing data between threads and not using lockers?

    Thanks in advance.


    Tuesday, August 13, 2019 12:56 PM

All replies

  • If you have thread(s) that writes shared data and thread(s) that read data same time, then you need some kind of an synchronization so that data remains valid.

    You should possibly describe more the use case. Is there single writer and multiple readers? Single reader and multiple writers or what? How big is the data set? How and in what kind of an structure data is saved.


    • Edited by MasaSam Tuesday, August 13, 2019 1:18 PM
    Tuesday, August 13, 2019 1:17 PM
  • Hi,

    I would try to not share any data if performance is your goal.

    You could try to split up your shared data in chunks, and process every chunk with a dedicated thread. In the end, you can add all the chunks back together for your final result.

    Kind regards,

    Johnny Hooyberghs

    Tuesday, August 13, 2019 1:40 PM
  • What I have is a class that represents a "physical object" (with data like coordinates, size...), and I have many of these objects.

    I have to study all of these "objects", and as it takes too much time to excecute, I want to study some of these objects at the same time.

    When an object is being studied it have to be compared to all of the other objects.

    And what happens when I am trying multi threading is that the program is mixing the data of the objects between threads. And this is what I dont understand, because I am creating for each thread an instance of a class (called "Calculations") and for each instance I pass through it the corresponding objects.

    Wednesday, August 14, 2019 7:25 AM
  • Hi Javier Waldo, 

    Thank you for posting here.

    For your question, you want to avoid using locks and sharing data between threads when using multithreads.

    >>but it always ends up mixing the data on results

    Could you provide some related code here? It will help us to make a test.

    We are waiting for your update.

    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.

    Wednesday, August 14, 2019 7:30 AM
    Moderator
  • Ok, here is the class that represents the physical objects (called trackers):

    class Tracker
        {
           
            //Tracker class
            
            //Basic characteristics
            public double[] northPost { get; set; } = new double[3]; //x,y,z coordinates of north post of the tracker 
            public double[] southPost { get; set; } = new double[3]; //x,y,z coordinates of south post of the tracker
            public double A { get; set; } //tracker width
            public double R { get; set; } //tracker pitch
            public double maxTilt { get; set; } //tilt limit
            
    
            public int Nx { get; set; } //cell divisions horizontal
            public int Ny { get; set; }//cell divisions vertical
    
            public List<double> azimut { get; set; }
            public List<double> elevation { get; set; }
            public List<double> SHFdirect { get; set; } //shadow factor on direct irradiance
            public List<double> SHFdiff { get; set; } //shadow factor on diffuse irradiance
            public List<double> SHFalb { get; set; } //shadow factor on albedo
    
            //Coordinates of tracker points (are going to be erased when not used)
            public double[,] cornerCoordinates; //corners coordintaes
            public double[,,] trackerCells; //each cell division coordinate (a cell is a division on the tacker to perform calculations)
            public double tilt; //tilt
            public double[] normal; //normal vector of tracker plane

    And here is part of the code that excecutes the threads and its inputs (each thread excecutes the class StudyGroup(), that performs the calculations for the group of trackers it gets as inputs:

     private async void button1_Click(object sender, EventArgs e)
            {
                var watch = System.Diagnostics.Stopwatch.StartNew();
                ListofTrackers listofTrackers = new ListofTrackers();
                string path = @"Q:\FV\MOD-03 (Shadings Simulation)\FASE 2 (RayTracing)\01 - Modelo\postes\nuevoformato.txt";
                string resultspath0 = @"Q:\FV\MOD-03 (Shadings Simulation)\FASE 2 (RayTracing)\01 - Modelo\tracker_results\";
                double A = 3;
                double R = 8;
                double maxTilt = 55.0 * Math.PI / 180;
                //import data
                listofTrackers.ImportTrackers(path, A, R, maxTilt);
    
                progressBar1.Maximum = listofTrackers.listofTrackers.Count();
                //------------
                ListofTrackers lt = new ListofTrackers();
                List<List<Tracker>> listofGroups = lt.splitList(1, listofTrackers.listofTrackers);
    
                Task[] tasks = new Task[listofGroups.Count()];
               
                foreach (List<Tracker> lot in listofGroups)
                {
    
                    var group = new Group();
                    group.listofGroups = listofGroups;
                    group.evaluableTrackers = listofTrackers;
                    group.groupN = listofGroups.IndexOf(lot);
    
                    tasks[group.groupN] = new Task(() =>
                    {
    
                        group.StudyGroup(resultspath0);
                    });
    
    
                    tasks[group.groupN].Start();
    
                }
    
                await Task.WhenAll(tasks);
                watch.Stop();
                var elapsedMs = watch.ElapsedMilliseconds;
    
                label1.Text = (Convert.ToString(elapsedMs) + "ms");
                label2.Text = "Simulation finished";
            }

    Wednesday, August 14, 2019 7:45 AM
  • Maybe it would be helpfull to know if there is a way to write the classes to make them thread safe without the need of lockers.
    Wednesday, August 14, 2019 7:57 AM
  • Hi Javier Waldo,

    Thanks for your feedback.

    I note that you have created a new thread and asked the same question in the forum, so you can refer the answers there.

    Thank you for your support.

    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.

    Monday, August 19, 2019 9:13 AM
    Moderator
  • Thread Safe Concurrent Collection in C#


    The .NET Framework 4 brought the System.Collections.Concurrent namespace. This has several collection classes that are thread-safe and scalable. These collections are called concurrent collections because they can be accessed by multiple threads at a time.

    The following are the concurrent collection in C# −

    BlockingCollection<T>

    Bounding and blocking functionality for any type.

    2

    ConcurrentDictionary<TKey,TValue>

    Thread-safe implementation of a dictionary of key-value pairs.

    3

    ConcurrentQueue<T>

    Thread-safe implementation of a FIFO (first-in, first-out) queue.

    4

    ConcurrentStack<T>

    Thread-safe implementation of a LIFO (last-in, first-out) stack.

    5

    ConcurrentBag<T>

    Thread-safe implementation of an unordered collection of elements.

    6

    IProducerConsumerCollection<T>

    The interface that a type must implement to be used in a BlockingCollection


    • Edited by ThisNewbie Monday, August 19, 2019 12:03 PM
    Monday, August 19, 2019 12:01 PM
  • There is no way to avoid locking if you need to be able to read data on one thread while writing data on another (short of dirty reads). However it is important to note that locking unto itself is actually pretty fast. The performance issues with locking always boil down to the length of the lock and that is most likely the issue here.

    The first option is to avoid reading/writing on separate threads at the same time. Depending upon your system you can do this any # of ways. The first option is to have the writes occur on any thread you want but not actually add the data such that it can be read until you're done writing it. This is great for things like processing of incoming requests. Since writes are just "setting up" the request to be processed that can occur on any thread. Once the writing is done it is pushed to shared queue where other thread(s) can grab the work (separately) and process the request in a single thread. The only lock needed here is for adding the request to a shared queue and the lock only lasts long enough to add it. 

    If you need to be able to write to an object at any point then the next question becomes, how do you notify the readers when there is new data to read? In most cases the previous option (a processing queue) solves this issue. However if you don't really care about being notified because you are continually re-processing the data (in a reader) then changing how long you lock becomes critical. Part of this will depend upon the size of the data. The great struggle in computing comes up here - speed vs size. You can get rid of most locking if you can copy the data you care about. Then, once again, you lock your object long enough to copy the data and then your readers can read the data without locking. Any changes made after the copy are lost. If you are dealing with small sets of data then this works well. 

    Batching would be a related concept and it involves locking only a portion of your data at a time. This is how databases tend to work. It requires that each object be "lockable" but you can have different threads reading and writing to different objects at the same time. Provided they don't try to read/write the same object at the same time they won't collide and hence the lock doesn't have an impact. Reader writer locks are designed for this situation. But you would really need to batch the data. You don't want to create a lock for every single object as it would be very expensive (unless you locked on the object itself). In batching you'd grab a set of objects (sort of like rows in a database) and then work on them as a single unit. This cuts down on the locks needed but still gives you better performance than locking the entire collection at once. This works really well if related data is sequential as you can lock a block of data and do all the processing without having to lock additional data.

    Finally, remember that it is the length of the lock that slows things down, not the lock itself. Whenever you lock something you need to unlock it as quickly as possible. If you're continually locking and unlocking the same data then it'll get costly. So lock the data, do the minimal work you need to against that locked data and then unlock it. For example if you need to do some calculations against some data points in an object then lock the object, grab the data points you need and then unlock it. Then run your calculation outside the lock. This may or may not work in all cases though it depends upon what you're going to do with the results.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, August 19, 2019 1:51 PM
    Moderator