C# Producer-Consumer using Semaphores - c#

Inspired by The Little Book of Semaphores, I decided to implement the Producer-Consumer problem using Semaphores.
I specifically want to be able to stop all Worker threads at will.
I've tested my methodolodgy extensively and can't find anything faulty.
Following code is a prototype for testing and can be ran as a Console application:
using System;
using System.Collections.Concurrent;
using System.Threading;
using NUnit.Framework;
public class ProducerConsumer
{
private static readonly int _numThreads = 5;
private static readonly int _numItemsEnqueued = 10;
private static readonly Semaphore _workItems = new Semaphore(0, int.MaxValue);
private static readonly ManualResetEvent _stop = new ManualResetEvent(false);
private static ConcurrentQueue<int> _queue;
public static void Main()
{
_queue = new ConcurrentQueue<int>();
// Create and start threads.
for (int i = 1; i <= _numThreads; i++)
{
Thread t = new Thread(new ParameterizedThreadStart(Worker));
// Start the thread, passing the number.
t.Start(i);
}
// Wait for half a second, to allow all the
// threads to start and to block on the semaphore.
Thread.Sleep(500);
Console.WriteLine(string.Format("Main thread adds {0} items to the queue and calls Release() {0} times.", _numItemsEnqueued));
for (int i = 1; i <= _numItemsEnqueued; i++)
{
Console.WriteLine("Waking up a worker thread.");
_queue.Enqueue(i);
_workItems.Release(); //wake up 1 worker
Thread.Sleep(2000); //sleep 2 sec so it's clear the threads get unblocked 1 by 1
}
// sleep for 5 seconds to allow threads to exit
Thread.Sleep(5000);
Assert.True(_queue.Count == 0);
Console.WriteLine("Main thread stops all threads.");
_stop.Set();
// wait a while to exit
Thread.Sleep(5000);
Console.WriteLine("Main thread exits.");
Console.WriteLine(string.Format("Last value of Semaphore was {0}.", _workItems.Release()));
Assert.True(_queue.Count == 0);
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
}
private static void Worker(object num)
{
// Each worker thread begins by requesting the semaphore.
Console.WriteLine("Thread {0} begins and waits for the semaphore.", num);
WaitHandle[] wait = { _workItems, _stop };
int signal;
while (0 == (signal = WaitHandle.WaitAny(wait)))
{
Console.WriteLine("Thread {0} becomes unblocked by Release() and has work to do.", num);
int res;
if (_queue.TryDequeue(out res))
{
Console.WriteLine("Thread {0} dequeues {1}.", num, res);
}
else
{
throw new Exception("this should not happen.");
}
}
if (signal == 1)
Console.WriteLine("Thread {0} was stopped.", num);
Console.WriteLine("Thread {0} exits.", num);
}
}
Now for my question, I'm using WaitHandle.WaitAny(semaphore) under the assumption that when I call Release() on the semaphore, only 1 Worker will be woken up. However, I can't find reassurance in the documentation that this is actually true. Can anyone confirm this is true?

It is indeed interesting that it seems that the documentation doesn't state explicitly that in the case of WaitOne only 1 thread will receive the signal. When you get familiar with multithreading theory this becomes somewhat self-evident.
Yes, WaitOne that is called on Semaphore (and WaitAny that is called on a list of WaitHandles that include Semaphore) is received by a single thread. If you want reference from MSDN so here it is, Semaphore is child class of WaitHandle, which is:
Encapsulates operating system–specific objects that wait for exclusive access to shared resources.
So yes, unless explicitly stated methods provide exclusive access.
For example method WaitOne of ManualResetEvent will unblock for all waiting threads, but documentation is explicit about it:
Notifies one or more waiting threads that an event has occurred.

Related

Keep .NET console application alive until the termination sequence finishes

I am working on a data acquisition application and I want to ensure that it exits gracefully. That is, it processes all the already collected data, flushes all the (file) buffers to "disk" (persistent memory) and might even uploads the data to the cloud.
So, I wrote (based on this answer) the code below to catch every close event. (It is just a test code.)
Problem: If I use the X in the top-right corner of the console, the program gets terminated after a short delay, even though the termination sequence is still running. (The handler does get called, and it does start to wait for the threads to join but then it gets killed after a while.) If I terminate with Crt+C or Ctr+Break it works as intended; The termination sequence finishes and exits the process.
Question: How can I make the OS wait for my application to terminate instead of killing it off after a short grace period?
#region Trap application termination
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler _handler;
enum CtrlType
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
private static bool Handler(CtrlType sig, List<Thread> threads, List<Task> tasks, CancellationTokenSource cancellationRequest)
{
//starts new foregeound thread, so the process doesn't terminate when all the cancelled threads end
Thread closer = new Thread(() => terminationSequence(threads, tasks, cancellationRequest));
closer.IsBackground = false;
closer.Start();
closer.Join(); //wait for the termination sequence to finish
return true; //just to be pretty; this never runs (obviously)
}
private static void terminationSequence(List<Thread> threads, List<Task> tasks, CancellationTokenSource cancellationRequest)
{
cancellationRequest.Cancel(); //sends cancellation requests to all threads and tasks
//wait for all the tasks to meet the cancellation request
foreach (Task task in tasks)
{
task.Wait();
}
//wait for all the treads to meet the cancellation request
foreach (Thread thread in threads)
{
thread.Join();
}
/*maybe do some additional work*/
//simulate work being done
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine("Spinning");
while (stopwatch.Elapsed.Seconds < 30)
{
if (stopwatch.Elapsed.Seconds % 2 == 0)
{
Console.Clear();
Console.WriteLine("Elapsed Time: {0}m {1}s", stopwatch.Elapsed.Minutes, stopwatch.Elapsed.Seconds);
}
Thread.SpinWait(10000);
}
Environment.Exit(0); //exit the process
}
#endregion
static void Main(string[] args)
{
CancellationTokenSource cancellationRequest = new CancellationTokenSource(); //cancellation signal to all threads and tasks
List<Thread> threads = new List<Thread>(); //list of threads
//specifys termination handler
_handler += new EventHandler((type) => Handler(type, threads, new List<Task>(), cancellationRequest));
SetConsoleCtrlHandler(_handler, true);
//creating a new thread
Thread t = new Thread(() => logic(cancellationRequest.Token));
threads.Add(t);
t.Start();
}
Since C# 7.1 you can have an async Task Main() method. Using this, you can modify your handler to create a method, and wait on it in the Main method.
Sidenote: you should use tasks instead of threads wherever you can. Tasks manage your thread better, and they run of the ThreadPool. When you create a new Thread instance, it's assuming it'll be a long running task, and the windows will treat it differently.
So with that in mind, consider wrapping the TerminateSequence method in a task, instead of a thread, and make that task a member of your class. Now you won't have to wait for it in the context of the handler, you could instead wait for it in the Main method.
With the rest of your code remaining the same, you could do the following:
private Task _finalTask;
private static bool Handler(CtrlType sig, List<Thread> threads, List<Task> tasks, CancellationTokenSource cancellationRequest)
{
//starts new foregeound thread, so the process doesn't terminate when all the cancelled threads end
_finalTask = Task.Run(() => terminationSequence(threads, tasks, cancellationRequest));
}
// ...
static async Task Main(string[] args)
{
// ...
// Wait for the termination process
if(_finalProcess != null)
await _finalTask
}
If you're not working with C# 7.1 you can still do this, it'll just be a little less elegant. All you need to do is wait for it:
_finalTask?.Wait();
And that should do it.

Out of memory exception while playing with Monitor.Wait and Monitor.Pulse

I was playing with the Monitor class in .NET so I arrived at a piece of code that seem to be working but when I loop it for a while it throws an OutOfMemoryException.
I am running this on a 64 bit windows 8 developer machine with 8 GB of RAM and I the process never takes up more than a 100 MB of space on the RAM.
This is my code:
using System;
using System.Threading;
public class Program
{
public static void Main()
{
while (true) {
object theLock = new Object();
Thread threadA = new Thread(() =>
{
Console.WriteLine("Thread A before lock");
lock (theLock)
{
Console.WriteLine("Thread A locking, about to Wait");
Monitor.Wait(theLock);
}
Console.WriteLine("Thread A after lock");
});
Thread threadB = new Thread(() =>
{
Console.WriteLine("Thread B before lock");
lock (theLock)
{
Console.WriteLine("Thread B lockint, about to Pulse");
Monitor.Pulse(theLock);
}
Console.WriteLine("Thread B before lock");
});
threadA.Start();
threadB.Start();
GC.Collect();
}
}
}
I read here that it might be a fragmentation problem and I added the GC.Collect() at the end. However I am not allocating big chunks of space.
Then I decided to measure how many iterations does the loop go through approximately before it throws the exception and added a counter:
using System;
using System.Threading;
public class Program
{
public static void Main()
{
int counter = 0;
while (true) {
Console.WriteLine(counter);
counter++;
object theLock = new Object();
Thread threadA = new Thread(() =>
{
Console.WriteLine("Thread A before lock");
lock (theLock)
{
Console.WriteLine("Thread A locking, about to Wait");
Monitor.Wait(theLock);
}
Console.WriteLine("Thread A after lock");
});
Thread threadB = new Thread(() =>
{
Console.WriteLine("Thread B before lock");
lock (theLock)
{
Console.WriteLine("Thread B lockint, about to Pulse");
Monitor.Pulse(theLock);
}
Console.WriteLine("Thread B before lock");
});
threadA.Start();
threadB.Start();
GC.Collect();
}
}
}
That seems to slow down the throwing of the exception a lot. I measured 36000 iterations.
For each pair of threads, if thread A manages to acquire the lock before thread B, you'll end up with both threads completing, and everything can be cleaned up.
If thread B manages to acquire the lock before thread A, thread B will complete (having pulsed the monitor) but then thread A will acquire the monitor and wait forever for something to pulse it. So at that point you'll have:
A Thread object
An OS thread
The object that the thread is waiting on
... all of which are tied up forever, basically.
Given that, I'm not surprised that you're seeing problems.
It's not clear what you're trying to achieve, but that explains the symptoms. Never assume that just because you call threadA.Start() before threadB.Start(), the first thread will actually reach a certain point in code before the second thread.

Monitor.Pulse(this) does not trigger Monitor.Wait(this);

I'm trying to get Monitor.Pulse(this) to trigger Monitor.Wait(this) in my code. I think my Wait statements are all running at some point with no Pulse. I have 5 different threads run by 5 different objects, each representing a queue with different priority. I'm trying to get each thread to run with a certain priority without using the thread priority attribute (i.e. normal, abovenormal, etc.). Anyways, point is that each thread only runs once and then it seems they are stuck at the Monitor.Wait(this) part in the thread that runs for each queue. Does anyone know why the Monitor.Pulse(this) doesn't trigger the Monitor.Wait(this) and continue the cycle. Each thread should be triggered one after the other by the Monitor.Wait(this) and the while loop that uses the Global variable GlobalCount. I think the problem must occur in my Beta method in the first class (Msg class) at the top where this triggering occurs. Or in my main method, although I'm less sure of that part having an issue.
What happens is it will execute a few lines and then start a new line but won't print anything else. The code is still running. I also tried removing the Monitor.Pulse and Monitor.Wait and it partially works, but every time the delta object's beta method runs its thread it is replaced by the alpha method. Does anyone know why this is and how I can get Pulse and Wait to work?
Here is my code (ignore some of the comments):
// StopJoin.cs
using System;
using System.Threading;
using System.Collections;
public class Msg
{
string message;
int priority;
public Msg(string ms, int pr)
{message = ms;
priority = pr;}
// This method that will be called when the thread is started
public void Beta()
{
while(true){
//Console.WriteLine("asdfasdfs");
Console.WriteLine(message+":"+GlobalClass.globalCount);
lock(this) // Enter synchronization block
{
while((priority - 1) != GlobalClass.globalCount){
//Console.WriteLine(GlobalClass.globalCount);
try
{
// Waits for the Monitor.Pulse in WriteToCell
//Console.WriteLine("beginning");
//Monitor.Wait(this);
//Console.WriteLine("end");
}
catch (SynchronizationLockException e)
{
Console.WriteLine(e);
}
catch (ThreadInterruptedException e)
{
Console.WriteLine(e);
}
if(GlobalClass.globalCount >= 5)
GlobalClass.globalCount = 0;
}
Console.WriteLine(message+".Beta is running in its own thread.");
for(int i = 0;i<priority;i++)
{
Console.WriteLine("sending message...");
}
if(GlobalClass.globalCount < 5)
GlobalClass.globalCount = GlobalClass.globalCount + 1;
//Monitor.Pulse(this); // Pulse tells Cell.WriteToCell that
//Console.WriteLine(GlobalClass.globalCount);
}
}
}
}
public class Alpha
{
Msg the_message = new Msg("Alpha",1);
public void doWork()
{the_message.Beta();}
};
public class Charlie
{
Msg the_message = new Msg("Charlie",2);
public void doWork()
{the_message.Beta();}
};
public class Delta
{
Msg the_message= new Msg("Alpha",3);
public void doWork()
{the_message.Beta();}
};
public class Echo
{
Msg the_message= new Msg("Echo",4);
public void doWork()
{the_message.Beta();}
};
public class Foxtrot
{
Msg the_message= new Msg("Foxtrot",5);
public void doWork()
{the_message.Beta();}
};
static class GlobalClass
{
private static int global_count = 0;
public static int globalCount
{
get{return global_count;}
set{global_count = value;}
}
}
public class Simple
{
public static int Main()
{
GlobalClass.globalCount = 2;
long s = 0;
long number = 100000000000000000;
Console.WriteLine("Thread Start/Stop/Join Sample");
Alpha oAlpha = new Alpha();
Charlie oCh = new Charlie();
Delta oDe = new Delta();
Echo oEc = new Echo();
Foxtrot oFo = new Foxtrot();
// Create the thread object, passing in the Alpha.Beta method
// via a ThreadStart delegate. This does not start the thread.
Thread oThread = new Thread(new ThreadStart(oAlpha.doWork));
Thread aThread = new Thread(new ThreadStart(oCh.doWork));
Thread bThread = new Thread(new ThreadStart(oDe.doWork));
Thread cThread = new Thread(new ThreadStart(oEc.doWork));
Thread dThread = new Thread(new ThreadStart(oFo.doWork));
// Start the thread
oThread.Start();
aThread.Start();
bThread.Start();
cThread.Start();
dThread.Start();
// Spin for a while waiting for the started thread to become
// alive:
while (!oThread.IsAlive);
while (!aThread.IsAlive);
while (!bThread.IsAlive);
while (!cThread.IsAlive);
while (!dThread.IsAlive);
// Put the Main thread to sleep for 1 millisecond to allow oThread
// to do some work:
Thread.Sleep(1);
// Wait until oThread finishes. Join also has overloads
// that take a millisecond interval or a TimeSpan object.
oThread.Join();
aThread.Join();
bThread.Join();
cThread.Join();
dThread.Join();
Console.WriteLine();
Console.WriteLine("Alpha.Beta has finished");
/*
try
{
Console.WriteLine("Try to restart the Alpha.Beta thread");
oThread.Start();
}
catch (ThreadStateException)
{
Console.Write("ThreadStateException trying to restart Alpha.Beta. ");
Console.WriteLine("Expected since aborted threads cannot be restarted.");
}
*/
while(s<number)
s++;
// Request that oThread be stopped
oThread.Abort();
aThread.Abort();
bThread.Abort();
cThread.Abort();
dThread.Abort();
return 0;
}
}
I can see a number of problems with your code, but there are two main ones that will be affecting you. I've assumed that your commented out Monitor calls shouldn't be commented (else the code makes no sense).
Firstly, you create a new instance of Msg under each thread. The Beta method locks on the current instance of Msg (in the commented Monitor.Wait(this)), and so each instance is essentially waiting on itself - which will be an infinite wait, because the only Monitor.Pulse is later in the same method, and will never be reached.
Because some of your Msg instances will be created with a higher value for priority, they will skip the while loop entirely and should continue to call Monitor.Pulse, but there will be nothing waiting on that pulse.
Later in your Main method, you have the following:
while (!oThread.IsAlive) ;
while (!aThread.IsAlive) ;
while (!bThread.IsAlive) ;
while (!cThread.IsAlive) ;
while (!dThread.IsAlive) ;
This is flawed. Because there's no guarantee of the execution order of your threads, it's entirely possible for the above code to deadlock. If your oThread isn't started immediately, but dThread is scheduled and runs to completion, you could easily see a case where dThread is completed and "dead" before the final line above is reached.
All in all, I'm not clear on what your code is trying to achieve, but as it stands I'd expect it to deadlock every time.

Easy way to have X threads with ??? task?

I have an app that takes on unknown amount of task. The task are blocking (they wait on network) i'll need multiple threads to keep busy.
Is there an easy way for me to have a giant list of task and worker threads which will pull the task when they are idle? ATM i just start a new thread for each task, which is fine but i'd like some control so if there are 100task i dont have 100threads.
Assuming that the network I/O classes that you are dealing with expose Begin/End style async methods, then what you want to do is use the TPL TaskFactory.FromAsync method. As laid out in TPL TaskFactory.FromAsync vs Tasks with blocking methods, the FromAsync method will use async I/O under the covers, rather than keeping a thread busy just waiting for the I/O to complete (which is actually not what you want).
The way that Async I/O works is that you have a pool of threads that can handle the result of I/O when the result is ready, so that if you have 100 outstanding I/Os you don't have 100 threads blocked waiting for those I/Os. When the whole pool is busy handling I/O results, subsequent results get queued up automatically until a thread frees up to handle them. Keeping a huge pool of threads waiting like that is a scalability disaster- threads are hugely expensive objects to keep around idling.
here a msdn sample to manage through a threadpool many threads:
using System;
using System.Threading;
public class Fibonacci
{
public Fibonacci(int n, ManualResetEvent doneEvent)
{
_n = n;
_doneEvent = doneEvent;
}
// Wrapper method for use with thread pool.
public void ThreadPoolCallback(Object threadContext)
{
int threadIndex = (int)threadContext;
Console.WriteLine("thread {0} started...", threadIndex);
_fibOfN = Calculate(_n);
Console.WriteLine("thread {0} result calculated...", threadIndex);
_doneEvent.Set();
}
// Recursive method that calculates the Nth Fibonacci number.
public int Calculate(int n)
{
if (n <= 1)
{
return n;
}
return Calculate(n - 1) + Calculate(n - 2);
}
public int N { get { return _n; } }
private int _n;
public int FibOfN { get { return _fibOfN; } }
private int _fibOfN;
private ManualResetEvent _doneEvent;
}
public class ThreadPoolExample
{
static void Main()
{
const int FibonacciCalculations = 10;
// One event is used for each Fibonacci object
ManualResetEvent[] doneEvents = new ManualResetEvent[FibonacciCalculations];
Fibonacci[] fibArray = new Fibonacci[FibonacciCalculations];
Random r = new Random();
// Configure and launch threads using ThreadPool:
Console.WriteLine("launching {0} tasks...", FibonacciCalculations);
for (int i = 0; i < FibonacciCalculations; i++)
{
doneEvents[i] = new ManualResetEvent(false);
Fibonacci f = new Fibonacci(r.Next(20,40), doneEvents[i]);
fibArray[i] = f;
ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);
}
// Wait for all threads in pool to calculation...
WaitHandle.WaitAll(doneEvents);
Console.WriteLine("All calculations are complete.");
// Display the results...
for (int i= 0; i<FibonacciCalculations; i++)
{
Fibonacci f = fibArray[i];
Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN);
}
}
}

How to call SemaphoreSlim.Release() without risking app failure?

I'm trying to use the new SemaphoreSlim class in .NET 4.0 to rate-limit a fast-paced loop that can run indefinitely. In unit testing this, I've found that if the loop is tight enough and the degree of parallelism is high enough, the SemaphoreSlim can throw an uncatchable exception when you call Release(), even if you check the .Count property first and lock around the semaphore instance itself during the entire check-count/release sequence.
This exception takes down the app, period. There is no catching it as far as I can tell.
Digging deeper, I discovered that the SemaphoreSlim is trying to access it's own .AvailableWaitHandle property internally during the Release() call, it throws the exception there, not from me accessing the SemaphoreSlim instance itself. (I had to debug with Debug->Exceptions->Common Language Runtime Exceptions->Thrown all checked in Visual Studio to discover this; you can't catch it at runtime. See The Uncatchable Exception for more details.)
My question is, does anyone know of a bullet-proof way to use this class without risking immediate app termination in such a case?
Note: The semaphore instance is wrapped in a RateGate instance, the code for which can be found in this article: Better Rate Limiting in .NET.
UPDATE:
I'm adding complete console app code to reproduce. Both answers contributed to the solution; see below for explanation.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Linq;
using System.Text;
using System.Threading;
using PennedObjects.RateLimiting;
namespace RateGateForceTerminateDemo
{
class Program
{
static int _secondsToRun = 10;
static void Main(string[] args) {
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
OptimizeMaxThreads();
Console.WriteLine();
Console.WriteLine("Press any key to exit.");
Console.ReadKey(true);
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
Console.WriteLine("Unhandled exception, terminating={0}:{1}", e.IsTerminating, e.ExceptionObject.ToString());
Console.WriteLine("Press any key to terminate app.");
Console.ReadKey(true);
}
static void OptimizeMaxThreads() {
int processors = Environment.ProcessorCount;
int processorsSq = Convert.ToInt32(Math.Pow(processors,2));
int threads = 1;
double result;
Tuple<int, double> maxResult = new Tuple<int, double>(threads, 0);
while (threads <= processorsSq) {
Console.WriteLine("Running for {0}s with upper limit of {1} threads... ", _secondsToRun, threads);
result = TestThrottling(10000000, threads, _secondsToRun);
Console.WriteLine("Ok. Result is {0:N0} ops/s", result);
Console.WriteLine();
if(result > maxResult.Item2)
maxResult = new Tuple<int, double>(threads, result);
threads *= 2;
}
Console.WriteLine("{0} threads achieved max throughput of {1:N0}", maxResult.Item1, maxResult.Item2);
}
static double TestThrottling(int limitPerSecond, int maxThreads, int maxRunTimeSeconds) {
int completed = 0;
RateGate gate = new RateGate(limitPerSecond, TimeSpan.FromSeconds(1));
ParallelLoopResult res = new ParallelLoopResult();
ParallelOptions parallelOpts = new ParallelOptions() { MaxDegreeOfParallelism = maxThreads };
Stopwatch sw = Stopwatch.StartNew();
try {
res = Parallel.For<int>(0, 1000000000, parallelOpts, () => 0, (num, state, subtotal) =>
{
bool succeeded = gate.WaitToProceed(10000);
if (succeeded) {
subtotal++;
}
else {
Console.WriteLine("Gate timed out for thread {0}; {1:N0} iterations, elapsed {2}.", Thread.CurrentThread.ManagedThreadId, subtotal, sw.Elapsed);
// return subtotal;
}
if (sw.Elapsed.TotalSeconds > maxRunTimeSeconds) {
Console.WriteLine("MaxRunTime expired for thread {0}, last succeeded={1}, iterations={2:N0}, elapsed={3}.", Thread.CurrentThread.ManagedThreadId, succeeded, subtotal, sw.Elapsed);
state.Break();
}
return subtotal;
}, (subtotal) => Interlocked.Add(ref completed, subtotal));
}
catch (AggregateException aggEx) {
Console.WriteLine(aggEx.Flatten().ToString());
}
catch (Exception ex) {
Console.WriteLine(ex);
}
sw.Stop();
double throughput = completed / Math.Max(sw.Elapsed.TotalSeconds, 1);
Console.WriteLine("Done at {0}, finished {1:N0} iterations, IsCompleted={2}, LowestBreakIteration={3:N0}, ",
sw.Elapsed,
completed,
res.IsCompleted,
(res.LowestBreakIteration.HasValue ? res.LowestBreakIteration.Value : double.NaN));
Console.WriteLine();
//// Uncomment the following 3 lines to stop prevent the ObjectDisposedException:
//Console.WriteLine("We should not hit the dispose statement below without a console pause.");
//Console.Write("Hit any key to continue... ");
//Console.ReadKey(false);
gate.Dispose();
return throughput;
}
}
}
So using #dtb's solution it was still possible for a thread "a" get past the _isDisposed check, and yet have thread "b" dispose the semaphore before thread "a" hit Release(). I found that adding a lock around the _semaphore instance in both the ExitTimerCallback and Dispose methods. #Peter Ritchie's suggestion led me to additionally cancel and dispose the timer, before disposing the semaphore. These two things combination let the program complete and dispose of the RateGate properly with no exceptions.
Since I wouldn't have gotten this without that input I don't want to answer myself. But since StackOverflow is more useful when complete answers are available, I'll accept whichever person first posts a patch or pseudo-patch that successfully survives the console app above.
The problem is in the RateGate class you're using. It has an internal Timer that runs its code even after the RateGate instance is disposed. This code includes a call to Release on a disposed SemaphoreSlim.
Fix:
## -88,7 +88,8 ##
int exitTime;
while (_exitTimes.TryPeek(out exitTime)
&& unchecked(exitTime - Environment.TickCount) <= 0)
{
+ if (_isDisposed) return;
_semaphore.Release();
_exitTimes.TryDequeue(out exitTime);
}
Based on your comment, it sounds like you're spawned a bunch of threads to work with a RateGate object that gets disposed before those threads are done using it. i.e. the threads are still running after your code exits from the using block.
UPDATE: if you do what you described in your comment; but don't use a using block you don't get the problem. the exception I witnessed was actually an ObjectDisposedException; which would make sense if the RateGate was disposed before code was done using it...

Categories