Instruction
The purpose of the laboratory is to get acquainted with concurrent collections.
- Introduction
In today's class, we will learn about the mechanisms of concurrent collections. - For the first, mistakes (0,5)
You should create a solution DotNet3 (or download template , but remeber to change .net 3.1 to the newest one in projects properties ) for today's laboratory and in it a project called UnsafeCollection .
Insert the code below and run.using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace UnsafeCollection { class Program { static void Main(string[] args) { List<Thread> threads = new List<Thread>(); List<int> numbers = new List<int>(1000000); Random rand = new Random(); for (int i = 0; i < 100; i++) { var watek = new Thread(() => { for (int l = 0; l < 10000; l++) numbers.Add(rand.Next()); }); threads.Add(watek); watek.Start(); } foreach (var watek in threads) { watek.Join(); } Console.WriteLine($"The number of items in a regular list: {numbers.Count}"); Console.ReadLine(); } } }
The above code creates a list of 100 threads. Each thread adds 10,000 random items to the list.
Run the program several times. How many items are in the list?
Add timing mesure with Stopwatch . - ConcurrentBag (0,5)
Use the ConcurrentBag collection instead of a normal list and measure the time from starting threads to ending them. - Synchronization with lock (0,5)
For the first program (from point 2) apply synchronization with lock . Also measure the execution time. - Unique collection (0,5)
- Add a new project named "UniqueCollection"
- Add class Extensions
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; namespace UniqueCollection { public static class Extensions { public static bool IsUnique(this List<int> list) { if (list.Distinct().Count() == list.Count()) //if the number of unique is equal to the number of all, they are all unique. { return true; } // Console.WriteLine($"Disctinct number: {lista.Distinct().Count()} is different from the number of elements: {lista.Count()}"); return false; } public static bool IsUnique(this ConcurrentBag<int> cb) { if (cb.Distinct().Count() == cb.Count()) return true; else return false; } } }
- Insert Main code:
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; namespace UniqueCollection { class Program { static void Main(string[] args) { List<Thread> threads = new List<Thread>(); ConcurrentBag<int> numbers = new ConcurrentBag<int>(); Stopwatch sw = new Stopwatch(); sw.Start(); Random rand = new Random(); for (int i = 0; i < 100; i++) { var watek = new Thread(() => { for (int l = 0; l < 100; l++) { int number = rand.Next(10001); while (numbers.Any(x => x == number)) {
lock(rand) number = rand.Next(10001); }; numbers.Add(number); } }); threads.Add(watek); watek.Start(); } foreach (var thread in threads) { thread.Join(); } sw.Stop(); Console.WriteLine($"Elapsed miliseconds {sw.ElapsedMilliseconds} ms"); if (numbers.IsUnique()) //using extension method for CB Console.WriteLine("All numbers are unique"); else Console.WriteLine("Not all numbers are unique"); Console.ReadLine(); } } }
- Insert Main code:
- The sample code does not provide unique values. Use lock to modify the code to make the values unique.
- ConcurrentDictionary (0,5)
- Add new project "CollectionDictionary".
- Based on the above, use ConcurrentDictionary to make the collection unique.
- Compare with the times obtained in the previous examples.
- Queue based producer and consumer (1,5)
- Add a new project ProducerConsumerQ
- Use the code below and see the effect
using System; using System.Collections.Generic; using System.Threading; namespace ProducerConsumerQ { class Program { static void Main(string[] args) { var cq = new Queue<int>(); List<Thread> threads = new List<Thread>(); Random rand = new Random(); //the producer puts 100 elements into the collection at random intervals of up to 500ms var producer = new Thread(() => { for (int l = 0; l < 100; l++) { cq.Enqueue(l); Console.WriteLine($"I'm putting {l}"); Thread.Sleep(rand.Next(500)); } }); producer.Start(); for (int i = 0; i < 10; i++) { var thread = new Thread(() => { for (int l = 0; l < 10; l++) { try { int result2 = cq.Dequeue(); Console.WriteLine($"I'm getting {result2} "); } catch (Exception ex) { Console.WriteLine("Exception for pick-up: ", ex.Message); } Thread.Sleep(rand.Next(5000)); } }); threads.Add(thread); thread.Start(); } foreach (var thread in threads) { thread.Join(); } producer.Join(); } } }
- Rewrite the program to use the ConcurrentQueue mechanism
- In the case of ConcurrentQueue, there is no blocking Dequeue method, but there is a method that tries to read data:
bool success = cq.TryDequeue(out result2);
You can examine the state and, in case of failure, repeat the operation so that each thread reads its portion of data. Otherwise, the reading threads will end and unread data will remain in the queue. (0,5). - Proactive checking is bad. Instead of examining the read state, use a synchronization semaphore so that all items are read. (0,5)
- Producer and consumer based on BlockingCollection (1)
- Using the example above, rewrite it so that it uses the blocking collection
- blocking collection should have a capacity of 10 elements
- start consumer threads 5 seconds after starting the producer
- consumer threads should retrieve a number from the collection 10 times at random intervals of max 5s.
- When inserting and downloading data, write information about this fact to the consoles.
- Please don't forget the report!