Instruction

by Paweł Paduch published 2020/11/12 09:20:00 GMT+2, last modified 2021-12-03T11:09:34+02:00
.net WaitHandle
  1. Introduction
    In today's class, we will deal with the WaitHandle class synchronization mechanisms
    • AutoResetEvent
    • ManualResetEvent
  2. Pseudorandom numbers (1 point)
    • Download the starting  solution . You can also create projects and paste the codes below. 
    • Unpack to your working directory with Visual Studio projects
    • Open the solution (DotNet2.sln file) or from within Visual Studio (Ctrl+shift+O)
    • Threads of the "consumer" type and one thread of the "producer" type are created in the Program.cs file
    • using System;
      using System.Collections.Generic;
      using System.Threading;
      
      namespace ProducentConsument
      {
          class Program
          {
              static int counter = 0;
              static readonly int threadCount = 10;
              static readonly int iterations = 10;
      
      
              static void Main(string[] args)
              {
                  List<Thread> consumentThreads = new List<Thread>();
                  //Random rand = new Random(); //random common
                  for (int i = 0; i < threadCount; i++)
                  {
                      var consument = new Thread(() =>
                      {
                          //in .net framework random local seed based on time 
                          Random rand = new Random();
                          //random local seed based on thread ID (but Tread ID on ich run is the same)
                          //Random rand = new Random(Thread.CurrentThread.ManagedThreadId); 
                          //random local seed based on thread ID and time
                          //Random rand = new Random(Thread.CurrentThread.ManagedThreadId+(int)DateTime.Now.Ticks);
                          for (int j = 0; j < iterations; j++)
                          {
                              var sleepTime = rand.Next(1000);
                              Console.Write($"{sleepTime} ");
                              Thread.Sleep(sleepTime);
                              Console.WriteLine($"[{counter}] ");
                          }
                      });
                      consumentThreads.Add(consument);
                      consument.Start();
                  }
      
                  Thread producentThread = new Thread(() =>
                  {
                      Random randW = new Random(Thread.CurrentThread.ManagedThreadId);
                      try
                      {
                          while (true)
                          {
                              Thread.Sleep(randW.Next(100));
                              Interlocked.Increment(ref counter);
                          }
                      }
                      catch (ThreadInterruptedException)
                      {
                          Console.WriteLine("\nWriteing thread finished");
                      }
                  });
      
                  producentThread.Start();
      
                  foreach (var thread in consumentThreads)
                  {
                      thread.Join();
                  }
      
                  producentThread.Interrupt();
                  producentThread.Join();
      
                  Console.WriteLine("This is the end, press ENTER...");
                  Console.ReadLine();
              }
          }
      }
      
      
    • All threads share a counter variable. The producer writes down consecutive numbers - consumers read these numbers. Run the program and see how it works. There are many disturbing things here. 
      • For the first. Generating pseudo-random numbers in several threads are identical. Comment local Random with seed based on time (the default constructor does that).
        Uncomment local Random with seed  based on thread Id.
        You can also test Random global (it is not recommended). What are the differences in the generated numbers? 
      • For the Second, the lack of synchronization causes duplication and the loss of numbers written by consumers.

  3. WaitHandles using (1 point)
    • Declare two objects of the EventWaitHandle class
      • static EventWaitHandle writeing = new AutoResetEvent(true);
      • static EventWaitHandle reading = new AutoResetEvent(false);
    • Use the above objects to synchronize the reading and writing so that clients print out consecutive unique numbers
  4. Using ManualResetEvent (1 point) 
    • ManualResetEvent differs in that after calling WaitOne EventWaitHandle does not close and you have to close it manually with Reset (); 
    • Change EventWaitHandle writeing and reading into ManualResetEvent by adding method Reset() just after call WaitOne().
    • Observe the operation of the application. 
    • What can I do to use ManualReset to make the application run like with AutoReset ? Implement your idea and check how it works.

  5. Meeting (0,1 point)
    • Create a new Meetings project
    • Copy paset following code
    • using System;
      using System.Threading;
      
      namespace Meetings
      {
          class Program
          {
              private static readonly EventWaitHandle wh1 = new EventWaitHandle(false, EventResetMode.AutoReset);
              private static readonly EventWaitHandle wh2 = new EventWaitHandle(false, EventResetMode.AutoReset);
              private static readonly int iterations = 5;
              private static Random rand = new Random();
      
              static void Main(string[] args)
              {
                  var watek1 = new Thread(() =>
                  {
                      for (int i = 0; i < iterations; i++)
                      {
                          Thread.Sleep(rand.Next(2000));
                          // WaitHandle.SignalAndWait(wh1, wh2);
                          Console.WriteLine("Bum! Thread 1 iteration: " + i);
                      }
                  });
                  var watek2 = new Thread(() =>
                  {
                      for (int i = 0; i < iterations; i++)
                      {
                          Thread.Sleep(rand.Next(2000));
                          //  WaitHandle.SignalAndWait(wh2, wh1);
                          Console.WriteLine("Bum! Thread 2 iteration: " + i);
                      }
                  });
                  watek1.Start();
                  watek2.Start();
                  watek1.Join();
                  watek2.Join();
                  Console.WriteLine("This is the end. Press ENTER...");
                  Console.ReadLine();
              }
          }
      }
      
      
    • Run
    • Uncomment SignalAndWait 
    • Run again

  6. WaitHandles (1 point.)
    • Create a new SynchroWait console project 
    • Copy paste following code
    • using System;
      using System.Collections.Generic;
      using System.Threading;
      
      namespace SynchroWait
      {
          class Program
          {
              static List<Thread> threads = new List<Thread>();
              static readonly int iterations = 5;
              static readonly int threadCount = 10;
              static Random rand = new Random();
      
              static void Main(string[] args)
              {
                  for (int i = 0; i < threadCount; i++)
                  {
                      var thread = new Thread((o) =>
                      {
                          int threadNo = (int)o;
                          Thread.Sleep(rand.Next(1000));
                          Console.WriteLine($"Thread no {threadNo} has started");
                          for (int j = 0; j < iterations; j++)
                          {
                              Console.WriteLine($"Thread no {threadNo} working during iteration no {j}");
                              Thread.Sleep(rand.Next(1000));
                          }
                          Thread.Sleep(rand.Next(1000));
                          Console.WriteLine($"Thread no {threadNo} has finished");
                      });
                      threads.Add(thread);
                      thread.Start(i);
                  }
      
                  Console.WriteLine("---------------- we lived to see it -----------------");
      
                  foreach (var w in threads)
                  {
                      w.Join();
                  }
      
                  Console.WriteLine("This is the end, press ENTER...");
                  Console.ReadLine();
              }
          }
      }
      
      
    • Run
    • Use WaitHandle mechanisms so that the inscription we lived to see it is displayed after finishing work (end of threads is not required) of all threads.
    • Use WaitHandle mechanisms so that the inscription we lived to see it is displayed after one of the threads has finished working. 

  7. Mutex, Semaphore, Lock (0,9 point)
    • Add a new Mutexes console project
    • Copy paste following code
    • using System;
      using System.Collections.Generic;
      using System.Threading;
      
      namespace Mutexes
      {
          class Program
          {
      
              static List<Thread> threads = new List<Thread>();
      
              static readonly int iterations = 10000;
              static readonly int threadsCount = 100;
      
              static int couter = 0;
      
              static void Main(string[] args)
              {
                  #region without synchronization
      
                  for (int i = 0; i < threadsCount; i++)
                  {
                      var watek = new Thread((o) =>
                      {
                          int nrWatku = (int)o;
      
                          for (int j = 0; j < iterations; j++)
                          {
                              couter++;
                          }
      
                      });
                      threads.Add(watek);
                      watek.Start(i);
                  }
      
                  foreach (var w in threads)
                  {
                      w.Join();
                  }
      
                  Console.WriteLine($"Counter: {couter}");
                  #endregion
      
      
      
                  Console.WriteLine("This is the end, press ENTER...");
                  Console.ReadLine();
              }
          }
      }
      
      
    • Synchronize access to the counter variable with lock, mutex and semaphore
    • Use Stopwatch to measure the execution time of all threads without synchronization and with subsequent synchronizations with lock, mutex and semaphore. 

  8. Of course, we remember the report