Click here to Skip to main content
15,120,078 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
I have to check unsynchronized and synchronized methods and I decided to do that with following problem: let's say there're two purses and I am adding money to one, , adding to other. When thread is not synchronized I expect not correct values inside purses, then I synchronize them, each purse should have the coin that was added.

What I have tried:

Here's my code so far:

    static class Program
    {
        static void Main(string[] args)
        {
            var purse1 = new Purse();
            var purse2 = new Purse();

            Console.WriteLine("Start data:");
            Console.WriteLine($"Purse 1: {purse1.Total}");
            Console.WriteLine($"Purse 2: {purse1.Total}" + Environment.NewLine);
            
            Thread purseThread1 =
                new Thread(() => purse1.Add(2))
                {
                    Name = "Purse1"
                };
            purseThread1.Start();

            
            Thread purseThread2 =
                new Thread(() => purse2.Add(8))
                {
                    Name = "Purse2"
                };
            
            purseThread2.Start();

            purseThread1.Join();
            purseThread2.Join();

            Console.WriteLine("Results:");
            Console.WriteLine($"Purse 1: {purse1.Total}");
            Console.WriteLine($"Purse 2: {purse1.Total}" + Environment.NewLine);

            Console.ReadLine();
        }
        
    }
    public class Purse
    {
        private readonly List<int> _coins = new List<int>();
        public int Total = 0;
        
        public void Add(int numberOfCoins)
        {
            lock (this)
            {
                for (int i = 1; i < numberOfCoins; i++)
                {
                    _coins.Add(i);
                    Console.Write(i + "," + Environment.NewLine);
                }
                Total = _coins.Sum(c => c);
            }
        }
    }
}


I am not sure what exactly wrong I am doing here but the correct (sync) result should be 10 and 200. Could someone help me out here?
Posted
Updated 24-Sep-21 21:26pm
v3
Comments
Richard MacCutchan 24-Sep-21 8:54am
   
The result should be 1 and 28; see my Solution below.

Why should the result be 10 and 200?
You start coinThread before you start purseThread1 - so if your system has spare cores it's very likely that coinThread will complete before purseThread1 even starts!

Try adding some Console.WriteLine calls to the class methods, and you'll see what I mean.
   
Comments
Member 12885549 24-Sep-21 7:47am
   
I simplified the solution but still can't get it right :(
The results might be incorrect when two or more threads read and write into the same value or object (memory) without synchronization.

Your code creates 2 threads. Each of them works with different object (purse1, purse2). There's no memory sharing between them thus the result should be correct even without synchronization.

If you change the code so that the both threads would operate purse1 instance, then you could expect incorrect behavior - if you wouldn't synchronize shared memory access [method Add() in this case].

Mira
   
v2
C#
Console.WriteLine($"Purse 1: {purse1.Total}");
Console.WriteLine($"Purse 2: {purse1.Total}" + Environment.NewLine);
                                   ^ should be purse2
   
Why would the results be 10 and 200?

Look at your Add() code in the Purse class. You're adding from 1 to < 2, which will only ever be 1 in the list. That Sum result is going to be 1.

The second purse .Add() is called with 8, so you're adding 1 though 7 to the list, the Sum of which is going to be 28.
   

I am not sure what you are trying to do but if you wish to update a shared variable from different threads, here are a couple of suggestions.

For simple operations like addition or incrementing use the Interlocked class, it is very efficient.

C#
public async Task BucketsInterlocked()
       {
           int bucket1 = 0;
           int bucket2 = 0;
           int sharedBucket = 0;

           Task bucket1Task = Task.Run( () =>
           {
               for (int i = 0; i < 5; i++)
               {
                   bucket1+=i;
                   Thread.Sleep(15);//simulate work
                   Interlocked.Add(ref sharedBucket, i);
               }

           });
           Task bucket2Task = Task.Run( () =>
           {
               for (int i = 5; i < 10; i++)
               {
                   bucket2+=i;
                   Thread.Sleep(10);
                   Interlocked.Add(ref sharedBucket, i);
               }

           });
           //wait for both tasks to complete
           await Task.WhenAll(bucket2Task, bucket1Task);

       }

For more complex operations use the SemaphoreSlim class, set up to limit access to the shared variable to one thread at a time.
C#
public async Task BucketsSemaphoreSlim()
  {
      List<int> bucket1 = new List<int>();
      List<int> bucket2 = new List<int>();
      List<int> sharedBucket = new List<int>();
      //gate writing to the shared bucket
      SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
      Task bucket1Task = Task.Run(async () =>
        {
            for (int i = 0; i < 5; i++)
            {
                bucket1.Add(i);
                Thread.Sleep(15);//simulate work
                await semaphoreSlim.WaitAsync();
                sharedBucket.Add(i);
                semaphoreSlim.Release();
            }

        });
      Task bucket2Task = Task.Run(async () =>
      {
          for (int i = 5; i < 10; i++)
          {
              bucket2.Add(i);
              Thread.Sleep(10);
              await semaphoreSlim.WaitAsync();
              sharedBucket.Add(i);
              semaphoreSlim.Release();
          }

      });
      //wait for both tasks to complete
      await Task.WhenAll(bucket2Task, bucket1Task);

  }

I would not recommend the DIY creation and marshalling of threads. In my experience, it nearly always results in head banging.

   

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900