// construct the blocking queue public BlockingQueue_(int size) { queueSize = size; room = new T[size]; putIdx = takeIdx = 0; freeSlots = new SemaphoreAsync(size, size); filledSlots = new SemaphoreAsync(0, size); }
// test semaphore as a mutual exclusion lock using asynchronous acquires private static bool TestSemaphoreAsLockAsync() { const int SETUP_TIME = 50; const int RUN_TIME = 5 * 1000; int THREADS = 20; const int MIN_TIMEOUT = 1; const int MAX_TIMEOUT = 50; Thread[] tthrs = new Thread[THREADS]; int[] privateCounters = new int[THREADS]; int[] timeouts = new int[THREADS]; int[] cancellations = new int[THREADS]; int sharedCounter = 0; bool exit = false; ManualResetEventSlim start = new ManualResetEventSlim(); #if USE_OUR_SEMAPHORE var _lock = new SemaphoreAsync(1, 1); // our semaphore #else SemaphoreSlim _lock = new SemaphoreSlim(1); // BCL semaphore #endif // // Create and start acquirer/releaser threads // for (int i = 0; i < THREADS; i++) { int tid = i; tthrs[i] = new Thread(() => { //Console.WriteLine("->#{0}", tid); start.Wait(); Random rnd = new Random(tid); do { do { using (CancellationTokenSource cts = new CancellationTokenSource()) { try { var resultTask = _lock.WaitAsync(rnd.Next(MAX_TIMEOUT), cts.Token); if (rnd.Next(100) < 10) { cts.Cancel(); } if (resultTask.Result) { break; } timeouts[tid]++; } catch (AggregateException ae) { ae.Handle((e) => { if (e is TaskCanceledException) { cancellations[tid]++; return(true); } return(false); }); } catch (Exception ex) { Console.WriteLine("*** Exception type: {0}", ex.GetType()); } } } while (true); sharedCounter++; if (THREADS > 1) { if (rnd.Next(100) < 95) { Thread.Yield(); } else { Thread.Sleep(rnd.Next(MIN_TIMEOUT, MAX_TIMEOUT)); } } privateCounters[tid]++; _lock.Release(); if (THREADS > 1 && privateCounters[tid] % 100 == 0) { Console.Write("[#{0:D2}]", tid); } } while (!Volatile.Read(ref exit)); //Console.Write("\n<-#{0}", tid); }); tthrs[i].Start(); } Thread.Sleep(SETUP_TIME); Stopwatch sw = Stopwatch.StartNew(); start.Set(); int endTime = Environment.TickCount + RUN_TIME; //... do { Thread.Sleep(20); if (Console.KeyAvailable) { Console.Read(); break; } } while (Environment.TickCount < endTime); Volatile.Write(ref exit, true); int sharedSnapshot = Volatile.Read(ref sharedCounter); sw.Stop(); // Wait until all threads have been terminated. for (int i = 0; i < THREADS; i++) { tthrs[i].Join(); } // Compute results Console.WriteLine("\n\nPrivate counters:"); int sum = 0; for (int i = 0; i < THREADS; i++) { sum += privateCounters[i]; if (i != 0 && i % 3 == 0) { Console.WriteLine(); } else if (i != 0) { Console.Write(' '); } Console.Write("[#{0:D2}: {1}/{2}/{3}]", i, privateCounters[i], timeouts[i], cancellations[i]); } Console.WriteLine(); long unitCost = (sw.ElapsedMilliseconds * 1000000L) / sharedSnapshot; Console.WriteLine("--unit cost of acquire/release: {0} {1}", unitCost > 1000 ? unitCost / 1000 : unitCost, unitCost > 1000 ? "us" : "ns"); return(sum == sharedCounter); }
// test semaphore fairness asynchronous private static bool TestSemaphoreFairnessAsync() { const int SETUP_TIME = 50; const int THREADS = 50; const int MIN_TIMEOUT = 5; const int MAX_TIMEOUT = 20; Thread[] tthrs = new Thread[THREADS]; int[] privateCounters = new int[THREADS]; ManualResetEventSlim startEvent = new ManualResetEventSlim(false); var sem = new SemaphoreAsync(THREADS, THREADS); int totalTimeouts = 0; bool exit = false; for (int i = 0; i < THREADS; i++) { int tid = i; tthrs[i] = new Thread(() => { Random random = new Random(tid); // Wait until start event is set startEvent.Wait(); do { do { if (sem.WaitAsyncEx(random.Next(MIN_TIMEOUT, MAX_TIMEOUT), CancellationToken.None).Result) { break; } Interlocked.Increment(ref totalTimeouts); } while (true); Thread.Yield(); sem.Release(); if ((++privateCounters[tid] % 100) == 0) { Console.Write("[#{0}]", tid); } } while (!Volatile.Read(ref exit)); }); tthrs[i].Start(); } // Wait until all test threads have been started and then set the // start event. Thread.Sleep(SETUP_TIME); startEvent.Set(); do { Thread.Sleep(50); } while (!Console.KeyAvailable); Console.Read(); Volatile.Write(ref exit, true); // Wait until all threads have been terminated. for (int i = 0; i < THREADS; i++) { tthrs[i].Join(); } // Show results int total = 0; Console.WriteLine("\nPrivate counters:"); for (int i = 0; i < THREADS; i++) { if (i != 0 && (i % 5) == 0) { Console.WriteLine(); } Console.Write("[#{0:D2}:{1,4}]", i, privateCounters[i]); total += privateCounters[i]; } Console.WriteLine("\n-- total acquisitions/releases: {0}, timeouts: {1}", total, totalTimeouts); return(true); }
// test semaphore as a mutual exclusion lock using synchronous acquires private static bool TestSemaphoreAsLock() { const int SETUP_TIME = 50; const int RUN_TIME = 10 * 1000; int THREADS = 20; const int MIN_TIMEOUT = 0; const int MAX_TIMEOUT = 10; const int MIN_CANCEL_INTERVAL = 10; const int MAX_CANCEL_INTERVAL = 50; Thread[] tthrs = new Thread[THREADS]; int[] privateCounters = new int[THREADS]; int[] timeouts = new int[THREADS]; int[] cancellations = new int[THREADS]; int[] issuedInterrupts = new int[THREADS]; int[] sensedInterrupts = new int[THREADS]; int sharedCounter = 0; bool exit = false; ManualResetEventSlim start = new ManualResetEventSlim(); // the semaphore #if USE_OUR_SEMAPHORE var _lock = new SemaphoreAsync(1, 1); // our semaphore #else SemaphoreSlim _lock = new SemaphoreSlim(1); // BCL semaphore #endif // // Create and start acquirer/releaser threads // for (int i = 0; i < THREADS; i++) { int tid = i; tthrs[i] = new Thread(() => { Random rnd = new Random(Thread.CurrentThread.ManagedThreadId); start.Wait(); CancellationTokenSource cts = new CancellationTokenSource(rnd.Next(MIN_CANCEL_INTERVAL, MAX_CANCEL_INTERVAL)); do { do { try { if (_lock.Wait(rnd.Next(MIN_TIMEOUT, MAX_TIMEOUT), cts.Token)) { break; } timeouts[tid]++; } catch (OperationCanceledException) { cancellations[tid]++; cts.Dispose(); cts = new CancellationTokenSource(rnd.Next(MIN_CANCEL_INTERVAL, MAX_CANCEL_INTERVAL)); } catch (ThreadInterruptedException) { sensedInterrupts[tid]++; } } while (true); sharedCounter++; if (THREADS > 1) { if (rnd.Next(100) < 95) { Thread.Yield(); } else { try { Thread.Sleep(rnd.Next(MIN_TIMEOUT, MAX_TIMEOUT)); } catch (ThreadInterruptedException) { sensedInterrupts[tid]++; } } } // release the lock filtering interrupts do { try { _lock.Release(); break; } catch (ThreadInterruptedException) { sensedInterrupts[tid]++; } } while (true); privateCounters[tid]++; if (THREADS > 1) { try { if ((privateCounters[tid] % 100) == 0) { Console.Write("[#{0:D2}]", tid); } } catch (ThreadInterruptedException) { sensedInterrupts[tid]++; } } } while (!Volatile.Read(ref exit)); try { Thread.Sleep(100); } catch (ThreadInterruptedException) { sensedInterrupts[tid]++; } }); tthrs[i].Start(); } Thread.Sleep(SETUP_TIME); Stopwatch sw = Stopwatch.StartNew(); start.Set(); Random grnd = new Random(Thread.CurrentThread.ManagedThreadId); int endTime = Environment.TickCount + RUN_TIME; //... do { Thread.Sleep(grnd.Next(100)); if (THREADS > 1) { var tid = grnd.Next(THREADS); tthrs[tid].Interrupt(); issuedInterrupts[tid]++; } if (Console.KeyAvailable) { Console.Read(); break; } } while (Environment.TickCount < endTime); Volatile.Write(ref exit, true); int sharedSnapshot = Volatile.Read(ref sharedCounter); sw.Stop(); // Wait until all threads have been terminated. for (int i = 0; i < THREADS; i++) { tthrs[i].Join(); } // Compute results Console.WriteLine("\nPrivate counters:"); int sum = 0, sumInterrupts = 0, totalIssuedInterrupts = 0; for (int i = 0; i < THREADS; i++) { sum += privateCounters[i]; sumInterrupts += sensedInterrupts[i]; totalIssuedInterrupts += issuedInterrupts[i]; if (i != 0 && (i % 3) == 0) { Console.WriteLine(); } else if (i != 0) { Console.Write(' '); } Console.Write("[#{0:D2}: {1}/{2}/{3}/{4}]", i, privateCounters[i], timeouts[i], cancellations[i], sensedInterrupts[i]); } Console.WriteLine("\n--shared/private: {0}/{1}", sharedCounter, sum); Console.WriteLine("--interrupts issuded/sensed: {0}/{1}", totalIssuedInterrupts, sumInterrupts); long unitCost = (sw.ElapsedMilliseconds * 1000000L) / sharedSnapshot; Console.Write("--time per acquisition/release: {0} {1}", unitCost >= 1000 ? unitCost / 1000 : unitCost, unitCost >= 1000 ? "us" : "ns"); for (var i = 0; i < THREADS; ++i) { Console.WriteLine($"issued:{issuedInterrupts[i]}, sensed:{sensedInterrupts[i]}"); } return(sum == sharedCounter && totalIssuedInterrupts == sumInterrupts); }