The following C# class is used in a multithreaded enviroment. I removed very much of the actual code. The problem occurs when calling MethodA and MethodB almost simultaneously. The order of the lock in the IsDepleted property doesn't solves the problem. Removing lock(WaitingQueue) from the IsDepleted property solves the deadlock, but this solution causes a problem when another thread adds/removes an item from the WaitingQueue between the WaitingQueue.Count == 0 and Processing.Count == 0 statements.
using System.Collections.Generic;
class Example
{
bool IsDepleted
{
get
{
lock (Processing)
{
lock (WaitingQueue)
{
return WaitingQueue.Count == 0
&& Processing.Count == 0;
}
}
}
}
private readonly List<object> Processing = new List<object>();
private readonly Queue<object> WaitingQueue = new Queue<object>();
public void MethodA(object item)
{
lock (WaitingQueue)
{
if (WaitingQueue.Count > 0)
{
if (StartItem(WaitingQueue.Peek()))
{
WaitingQueue.Dequeue();
}
}
}
}
public void MethodB(object identifier)
{
lock (Processing)
{
Processing.Remove(identifier);
if (!IsDepleted)
{
return;
}
}
//Do something...
}
bool StartItem(object item)
{
//Do something and return a value
}
}
It depends if you want a quick fix or a rigorous fix.
A quick fix would be just to use one lock object in all cases.
e.g. private readonly object _lock = new object();
And then just lock on that. However, depending on your situation, that may impact performance more than you can accept.
I.e. your code would become this:
using System.Collections.Generic;
class Example
{
private readonly object _lock = new object();
bool IsDepleted
{
get
{
lock (_lock)
{
return WaitingQueue.Count == 0
&& Processing.Count == 0;
}
}
}
private readonly List<object> Processing = new List<object>();
private readonly Queue<object> WaitingQueue = new Queue<object>();
public void MethodA(object item)
{
lock (_lock)
{
if (WaitingQueue.Count > 0)
{
if (StartItem(WaitingQueue.Peek()))
{
WaitingQueue.Dequeue();
}
}
}
}
public void MethodB(object identifier)
{
lock (_lock)
{
Processing.Remove(identifier);
if (!IsDepleted)
{
return;
}
}
//Do something...
}
bool StartItem(object item)
{
//Do something and return a value
}
}
Take the Processing lock in method A and the WaitingQueue lock in method B (in other words, make it look like the first block of code). That way, you always take the locks in the same order and you'll never deadlock.
Simplify your code and use only a single object to lock on. You could also replace your locks with:
Monitor.TryEnter(Processing,1000)
this will give you a 1 second timeout. So essentially:
if (Monitor.TryEnter(Processing, 1000))
{
try
{
//do x
}
finally
{
Monitor.Exit(Processing);
}
}
Now you won't stop the deadlock but you can handle the case where you don't get a lock.
Related
For sake of practice, I am trying to write a solution to the readers-writers problem.
The expected behavior should be that multiple reads can run concurrently, but writes need to wait for all readers to finish.
My solution is below in Read(), Write() methods, and the book I am referencing suggests Write2() for the writers.
1) I don't entirely understand why they chose to implement this way, specifically why the read lock is trying to be acquired again, after being awoken when numOfReaders == 0.
Is that to give readers priority, if one came right after Write acquired the read lock, and right before it actually wrote anything?
2) Are there any other issues with the my suggested Write implementation?
Thanks!!
class ReaderWriter
{
private int numOfReaders = 0;
private readonly object readLock = new object();
private readonly object writeLock = new object();
public void Read()
{
lock (readLock)
{
this.numOfReaders++;
}
// Read stuff
lock (readLock)
{
this.numOfReaders--;
Monitor.Pulse(readLock);
}
}
// My solution
public void Write()
{
lock (writeLock)
{
lock (readLock)
{
while (this.numOfReaders > 0)
{
Monitor.Wait(readLock);
}
// Write stuff
}
}
}
// Alternative solution
public void Write2()
{
lock (writeLock)
{
bool done = false;
while (!done)
{
lock (readLock)
{
if (this.numOfReaders == 0)
{
// Write stuff
done = true;
}
else
{
while (this.numOfReaders > 0)
{
Monitor.Wait(readLock);
}
}
}
}
}
}
}
I would like to run code alternatively, so I could stop execution at any moment. Is this code safe?
static class Program
{
static void Main()
{
var foo = new Foo();
//wait for interaction (this will be GUI app, so eg. btnNext_click)
foo.Continue();
//wait again etc.
foo.Continue();
foo.Continue();
foo.Continue();
foo.Continue();
foo.Continue();
}
}
class Foo
{
public Foo()
{
new Thread(Run).Start();
}
private void Run()
{
Break();
OnRun();
}
protected virtual void OnRun()
{
for (var i = 0; i < 5; i++)
{
Console.WriteLine(i);
Break();
}
//do something else and break;
}
private void Break()
{
lock (this)
{
Monitor.Pulse(this);
Monitor.Wait(this);
}
}
public void Continue()
{
lock (this)
{
Monitor.Pulse(this);
Monitor.Wait(this);
}
}
}
Of course I know, that now the application will never ends, but that's not the point.
I need this, because I would like to present steps in some kind of an algorithm and describe what is going on in particular moment, and making everything in one thread would lead to many complications even when using small amount of loops in the code. For example those lines:
for (var i = 0; i < 5; i++)
{
Console.WriteLine(i);
Break();
}
should be then replaced with:
if (this.i < 5)
{
Console.WriteLine(i++);
}
And that is just a small example of what I want to present. The code will be more complicated than a dummy for loop.
I recommend you check out this blog post about implementing fibers.
Code (In case the site goes down.)
public class Fiber
{
private readonly Stack<IEnumerator> stackFrame = new Stack<IEnumerator>();
private IEnumerator currentRoutine;
public Fiber(IEnumerator entryPoint)
{
this.currentRoutine = entryPoint;
}
public bool Step()
{
if (currentRoutine.MoveNext())
{
var subRoutine = currentRoutine.Current
as IEnumerator;
if (subRoutine != null)
{
stackFrame.Push(currentRoutine);
currentRoutine = subRoutine;
}
}
else if (stackFrame.Count > 0)
{
currentRoutine = stackFrame.Pop();
}
else
{
OnFiberTerminated(
new FiberTerminatedEventArgs(
currentRoutine.Current
)
);
return false;
}
return true;
}
public event EventHandler<FiberTerminatedEventArgs> FiberTerminated;
private void OnFiberTerminated(FiberTerminatedEventArgs e)
{
var handler = FiberTerminated;
if (handler != null)
{
handler(this, e);
}
}
}
public class FiberTerminatedEventArgs : EventArgs
{
private readonly object result;
public FiberTerminatedEventArgs(object result)
{
this.result = result;
}
public object Result
{
get { return this.result; }
}
}
class FiberTest
{
private static IEnumerator Recurse(int n)
{
Console.WriteLine(n);
yield return n;
if (n > 0)
{
yield return Recurse(n - 1);
}
}
static void Main(string[] args)
{
var fiber = new Fiber(Recurse(5));
while (fiber.Step()) ;
}
}
"...this will be GUI app..."
Then you probably do not want and will not have sequential code like above in Main().
I.e. the main GUI thread will not execute a serial code like above, but generally be idle, repainting, etc. or handling the Continue button click.
In that event handler you may better use an Auto|ManualResetEvent to signal the worker to proceed.
In the worker, just wait for the event.
I would suggest that any time one considers using Monitor.Wait(), one should write code so that it would work correctly if the Wait sometimes spontaneously acted as though it received a pulse. Typically, this means one should use the pattern:
lock(monitorObj)
{
while(notYetReady)
Monitor.Wait(monitorObj);
}
For your scenario, I'd suggest doing something like:
lock(monitorObj)
{
turn = [[identifier for this "thread"]];
Monitor.PulseAll(monitorObj);
while(turn != [[identifier for this "thread"]])
Monitor.Wait(monitorObj);
}
It is not possible for turn to change between its being checked whether it's the current thread's turn to proceed and the Monitor.Wait. Thus, if the Wait isn't skipped, the PulseAll is guaranteed to awaken it. Note that the code would work just fine if Wait spontaneously acted as though it received a pulse--it would simply spin around, observe turn wasn't set for the current thread, and go back to waiting.
I have a class running the Producer-Consumer model like this:
public class SyncEvents
{
public bool waiting;
public SyncEvents()
{
waiting = true;
}
}
public class Producer
{
private readonly Queue<Delegate> _queue;
private SyncEvents _sync;
private Object _waitAck;
public Producer(Queue<Delegate> q, SyncEvents sync, Object obj)
{
_queue = q;
_sync = sync;
_waitAck = obj;
}
public void ThreadRun()
{
lock (_sync)
{
while (true)
{
Monitor.Wait(_sync, 0);
if (_queue.Count > 0)
{
_sync.waiting = false;
}
else
{
_sync.waiting = true;
lock (_waitAck)
{
Monitor.Pulse(_waitAck);
}
}
Monitor.Pulse(_sync);
}
}
}
}
public class Consumer
{
private readonly Queue<Delegate> _queue;
private SyncEvents _sync;
private int count = 0;
public Consumer(Queue<Delegate> q, SyncEvents sync)
{
_queue = q;
_sync = sync;
}
public void ThreadRun()
{
lock (_sync)
{
while (true)
{
while (_queue.Count == 0)
{
Monitor.Wait(_sync);
}
Delegate query = _queue.Dequeue();
query.DynamicInvoke(null);
count++;
Monitor.Pulse(_sync);
}
}
}
}
/// <summary>
/// Act as a consumer to the queries produced by the DataGridViewCustomCell
/// </summary>
public class QueryThread
{
private SyncEvents _syncEvents = new SyncEvents();
private Object waitAck = new Object();
private Queue<Delegate> _queryQueue = new Queue<Delegate>();
Producer queryProducer;
Consumer queryConsumer;
public QueryThread()
{
queryProducer = new Producer(_queryQueue, _syncEvents, waitAck);
queryConsumer = new Consumer(_queryQueue, _syncEvents);
Thread producerThread = new Thread(queryProducer.ThreadRun);
Thread consumerThread = new Thread(queryConsumer.ThreadRun);
producerThread.IsBackground = true;
consumerThread.IsBackground = true;
producerThread.Start();
consumerThread.Start();
}
public bool isQueueEmpty()
{
return _syncEvents.waiting;
}
public void wait()
{
lock (waitAck)
{
while (_queryQueue.Count > 0)
{
Monitor.Wait(waitAck);
}
}
}
public void Enqueue(Delegate item)
{
_queryQueue.Enqueue(item);
}
}
The code run smoothly but the wait() function.
In some case I want to wait until all the function in the queue were finished running so I made the wait() function.
The producer will fire the waitAck pulse at suitable time.
However, when the line "Monitor.Wait(waitAck);" is ran in the wait() function, all thread stop, includeing the producer and consumer thread.
Why would this happen and how can I solve it? thanks!
It seems very unlikely that all the threads will actually stop, although I should point out that to avoid false wake-ups you should probably have a while loop instead of an if statement:
lock (waitAck)
{
while(queryProducer.secondQueue.Count > 0)
{
Monitor.Wait(waitAck);
}
}
The fact that you're calling Monitor.Wait means that waitAck should be released so it shouldn't prevent the consumer threads from locking...
Could you give more information about the way in which the producer/consumer threads are "stopping"? Does it look like they've just deadlocked?
Is your producer using Notify or NotifyAll? You've got an extra waiting thread now, so if you only use Notify it's only going to release a single thread... it's hard to see whether or not that's a problem without the details of your Producer and Consumer classes.
If you could show a short but complete program to demonstrate the problem, that would help.
EDIT: Okay, now you've posted the code I can see a number of issues:
Having so many public variables is a recipe for disaster. Your classes should encapsulate their functionality so that other code doesn't have to go poking around for implementation bits and pieces. (For example, your calling code here really shouldn't have access to the queue.)
You're adding items directly to the second queue, which means you can't efficiently wake up the producer to add them to the first queue. Why do you even have multiple queues?
You're always waiting on _sync in the producer thread... why? What's going to notify it to start with? Generally speaking the producer thread shouldn't have to wait, unless you have a bounded buffer
You have a static variable (_waitAck) which is being overwritten every time you create a new instance. That's a bad idea.
You also haven't shown your SyncEvents class - is that meant to be doing anything interesting?
To be honest, it seems like you've got quite a strange design - you may well be best starting again from scratch. Try to encapsulate the whole producer/consumer queue in a single class, which has Produce and Consume methods, as well as WaitForEmpty (or something like that). I think you'll find the synchronization logic a lot easier that way.
Here is my take on your code:
public class ProducerConsumer
{
private ManualResetEvent _ready;
private Queue<Delegate> _queue;
private Thread _consumerService;
private static Object _sync = new Object();
public ProducerConsumer(Queue<Delegate> queue)
{
lock (_sync)
{
// Note: I would recommend that you don't even
// bother with taking in a queue. You should be able
// to just instantiate a new Queue<Delegate>()
// and use it when you Enqueue. There is nothing that
// you really need to pass into the constructor.
_queue = queue;
_ready = new ManualResetEvent(false);
_consumerService = new Thread(Run);
_consumerService.IsBackground = true;
_consumerService.Start();
}
}
public override void Enqueue(Delegate value)
{
lock (_sync)
{
_queue.Enqueue(value);
_ready.Set();
}
}
// The consumer blocks until the producer puts something in the queue.
private void Run()
{
Delegate query;
try
{
while (true)
{
_ready.WaitOne();
lock (_sync)
{
if (_queue.Count > 0)
{
query = _queue.Dequeue();
query.DynamicInvoke(null);
}
else
{
_ready.Reset();
continue;
}
}
}
}
catch (ThreadInterruptedException)
{
_queue.Clear();
return;
}
}
protected override void Dispose(bool disposing)
{
lock (_sync)
{
if (_consumerService != null)
{
_consumerService.Interrupt();
}
}
base.Dispose(disposing);
}
}
I'm not exactly sure what you're trying to achieve with the wait function... I'm assuming you're trying to put some type of a limit to the number of items that can be queued. In that case simply throw an exception or return a failure signal when you have too many items in the queue, the client that is calling Enqueue will keep retrying until the queue can take more items. Taking an optimistic approach will save you a LOT of headaches and it simply helps you get rid of a lot of complex logic.
If you REALLY want to have the wait in there, then I can probably help you figure out a better approach. Let me know what are you trying to achieve with the wait and I'll help you out.
Note: I took this code from one of my projects, modified it a little and posted it here... there might be some minor syntax errors, but the logic should be correct.
UPDATE: Based on your comments I made some modifications: I added another ManualResetEvent to the class, so when you call BlockQueue() it gives you an event which you can wait on and sets a flag to stop the Enqueue function from queuing more elements. Once all the queries in the queue are serviced, the flag is set to true and the _wait event is set so whoever is waiting on it gets the signal.
public class ProducerConsumer
{
private bool _canEnqueue;
private ManualResetEvent _ready;
private Queue<Delegate> _queue;
private Thread _consumerService;
private static Object _sync = new Object();
private static ManualResetEvent _wait = new ManualResetEvent(false);
public ProducerConsumer()
{
lock (_sync)
{
_queue = new Queue<Delegate> _queue;
_canEnqueue = true;
_ready = new ManualResetEvent(false);
_consumerService = new Thread(Run);
_consumerService.IsBackground = true;
_consumerService.Start();
}
}
public bool Enqueue(Delegate value)
{
lock (_sync)
{
// Don't allow anybody to enqueue
if( _canEnqueue )
{
_queue.Enqueue(value);
_ready.Set();
return true;
}
}
// Whoever is calling Enqueue should try again later.
return false;
}
// The consumer blocks until the producer puts something in the queue.
private void Run()
{
try
{
while (true)
{
// Wait for a query to be enqueued
_ready.WaitOne();
// Process the query
lock (_sync)
{
if (_queue.Count > 0)
{
Delegate query = _queue.Dequeue();
query.DynamicInvoke(null);
}
else
{
_canEnqueue = true;
_ready.Reset();
_wait.Set();
continue;
}
}
}
}
catch (ThreadInterruptedException)
{
_queue.Clear();
return;
}
}
// Block your queue from enqueuing, return null
// if the queue is already empty.
public ManualResetEvent BlockQueue()
{
lock(_sync)
{
if( _queue.Count > 0 )
{
_canEnqueue = false;
_wait.Reset();
}
else
{
// You need to tell the caller that they can't
// block your queue while it's empty. The caller
// should check if the result is null before calling
// WaitOne().
return null;
}
}
return _wait;
}
protected override void Dispose(bool disposing)
{
lock (_sync)
{
if (_consumerService != null)
{
_consumerService.Interrupt();
// Set wait when you're disposing the queue
// so that nobody is left with a lingering wait.
_wait.Set();
}
}
base.Dispose(disposing);
}
}
I wrote a multithreaded application for .NET and in a very important portion of code I have the following:
public class ContainerClass {
private object list_lock;
private ArrayList list;
private object init_lock = new object();
private ThreadClass thread;
public void Start() {
lock(init_lock) {
if (thread == null) {
thread = new ThreadClass();
...
}
}
}
public void Stop() {
lock(init_lock) {
if (thread != null) {
thread.processList(0);
thread.finish();
thread.waitUntilFinished();
thread = null;
} else {
throw new ApplicationException("Assertion failed - already stopped.");
}
...
}
}
private class ThreadedClass {
private ContainerClass container;
private Thread thread;
private bool finished;
private bool actually_finished;
public ThreadedClass(ContainerClass container) {
this.container = container;
thread = new Thread(run);
thread.IsBackground = true;
thread.Start();
}
private void run() {
bool local_finished = false;
while (!local_finished) {
ArrayList to_process = null;
lock (container.list_lock) {
if (container.list.Count > 0) {
to_process = new ArrayList();
to_process.AddRange(container.list);
}
}
if (to_process == null) {
// Nothing to process so wait
lock (this) {
if (!finished) {
try {
Monitor.Wait(this);
} catch (ThreadInterruptedException) {
}
}
}
} else if (to_process.Count > 0) {
// Something to process, so go ahead and process the journals,
int sz = to_process.Count;
// For all elements
for (int i = 0; i < sz; ++i) {
// Pick the lowest element to process
object obj = to_process[i];
try {
// process the element...
...
} catch (IOException e) {
...
// If there is an error processing the best thing to do is finish
lock (this) {
finished = true;
}
}
}
}
lock (this) {
local_finished = finished;
// Remove the elements that we have just processed.
if (to_process != null) {
lock (container.list_lock) {
int sz = to_process.Count;
for (int i = 0; i < sz; ++i) {
container.list.RemoveAt(0);
}
}
}
// Notify any threads waiting
Monitor.PulseAll(this);
}
}
lock (this) {
actually_finished = true;
Monitor.PulseAll(this);
}
}
public void waitUntilFinished() {
lock (this) {
try {
while (!actually_finished) {
Monitor.Wait(this);
}
} catch (ThreadInterruptedException e) {
throw new ApplicationException("Interrupted: " + e.Message);
}
}
}
public void processList(int until_size) {
lock (this) {
Monitor.PulseAll(this);
int sz;
lock (container.list_lock) {
sz = container.list.Count;
}
// Wait until the sz is smaller than 'until_size'
while (sz > until_size) {
try {
Monitor.Wait(this);
} catch (ThreadInterruptedException ) {
}
lock (container.list_lock) {
sz = container.list.Count;
}
}
}
}
}
}
As you can see, the thread waits until the collection is empty but it seems that the synchronization clashes forbids the thread to enter at the point (the only one in the whole code) where an element is removed from the collection list in the ContainerClass.
This clash provokes the code to never return and the application to continue running if the method processList is called with the value of until_size of 0.
I beg any better developer than me (and I guess there are a lot out there) to help me fixing this small piece of code, since I really can't understand why the list isn't decremented...
Thank you very much from the bottom of my heart.
PS. I would like to underline that the code works perfectly for all the time: the only situation in which it brakes it's when calling thread.processList(0) from ContainerClass.Stop().
Could the problem be that you are locking the ThreadClass object itself rather than a synchronizing object?
Try adding another private variable to lock on:
private static readonly object lockObject = new object()
and replace all the calls of lock(this) with lock(lockObject)
MSDN clearly advises against what your doing:
In general, avoid locking on a public
type, or instances beyond your code's
control. The common constructs lock
(this), lock (typeof (MyType)), and
lock ("myLock") violate this
guideline:
lock (this) is a problem if the instance can be accessed publicly.
Edit:
I think I see a deadlock condition. If you call run() when there are no objects to process, or you get to no objects to process, you lock(this), then call Monitor.Wait(this) and the thread waits:
if (to_process == null) {
// Nothing to process so wait
lock (this) { /* nothing's going to get this lock again until Monitor.PulseAll(this) is called from somewhere */
if (!finished) {
try {
Monitor.Wait(this); /* thread is waiting for Pulse(this) or PulseAll(this) */
} catch (ThreadInterruptedException) {
}
}
}
}
If you are in this condition when you call Container.Stop(), when ThreadProcess.processList(int) is called, you call lock(this) again, which can't enter the section because the run() method still has the lock:
lock (this) { /* run still holds this lock, waiting for PulseAll(this) to be called */
Monitor.PulseAll(this); /* this isn't called so run() never continues */
int sz;
lock (container.list_lock) {
sz = container.list.Count;
}
So, Monitor.PulseAll() can't be called to free the waiting thread in the run() method to exit the lock(this) area, so they are deadlocked waiting on each other. Right?
I think you should try to explain better what you actually want to achieve.
public void processList(int until_size) {
lock (this) {
Monitor.PulseAll(this);
This looks very strange as you should call the Monitor.Pulse when changing the lock state and not when beginning with locking.
Where are you creating the worker threads - this section is not clear as I see only Thread.Start()?
Btw I would advise you to look at PowerCollections - maybe you find what you need there.
What is it and how to use?
I need that as I have a timer that inserts into DB every second, and I have a shared resource between timer handler and the main thread.
I want to gurantee that if the timer handler takes more than one second in the insertion the waited threads should be executed in order.
This is a sample code for my timer handler:
private void InsertBasicVaraibles(object param)
{
try
{
DataTablesMutex.WaitOne();//mutex for my shared resources
//insert into DB
}
catch (Exception ex)
{
//Handle
}
finally
{
DataTablesMutex.ReleaseMutex();
}
}
But currently the mutex does not guarantee any order.
You'll need to write your own class to do this, I found this example (pasted because it looks as though the site's domain has lapsed):
using System.Threading;
public sealed class QueuedLock
{
private object innerLock;
private volatile int ticketsCount = 0;
private volatile int ticketToRide = 1;
public QueuedLock()
{
innerLock = new Object();
}
public void Enter()
{
int myTicket = Interlocked.Increment(ref ticketsCount);
Monitor.Enter(innerLock);
while (true)
{
if (myTicket == ticketToRide)
{
return;
}
else
{
Monitor.Wait(innerLock);
}
}
}
public void Exit()
{
Interlocked.Increment(ref ticketToRide);
Monitor.PulseAll(innerLock);
Monitor.Exit(innerLock);
}
}
Example of usage:
QueuedLock queuedLock = new QueuedLock();
try
{
queuedLock.Enter();
// here code which needs to be synchronized
// in correct order
}
finally
{
queuedLock.Exit();
}
Source via archive.org
Just reading Joe Duffy's "Concurrent Programming on Windows" it sounds like you'll usually get FIFO behaviour from .NET monitors, but there are some situations where that won't occur.
Page 273 of the book says: "Because monitors use kernel objects internally, they exhibit the same roughly-FIFO behavior that the OS synchronization mechanisms also exhibit (described in the previous chapter). Monitors are unfair, so if another thread sneaks in and acquires the lock before an awakened waiting thread tries to acquire the lock, the sneaky thread is permitted to acquire the lock."
I can't immediately find the section referenced "in the previous chapter" but it does note that locks have been made deliberately unfair in recent editions of Windows to improve scalability and reduce lock convoys.
Do you definitely need your lock to be FIFO? Maybe there's a different way to approach the problem. I don't know of any locks in .NET which are guaranteed to be FIFO.
You should re-design your system to not rely on the execution order of the threads. For example, rather than have your threads make a DB call that might take more than one second, have your threads place the command they would execute into a data structure like a queue (or a heap if there is something that says "this one should be before another one"). Then, in spare time, drain the queue and do your db inserts one at a time in the proper order.
There is no guaranteed order on any built-in synchronisation objects: http://msdn.microsoft.com/en-us/library/ms684266(VS.85).aspx
If you want a guaranteed order you'll have to try and build something yourself, note though that it's not as easy as it might sound, especially when multiple threads reach the synchronisation point at (close to) the same time. To some extent the order they will be released will always be 'random' since you cannot predict in which order the point is reached, so does it really matter?
Actually the answers are good, but I solved the problem by removing the timer and run the method (timer-handler previously) into background thread as follows
private void InsertBasicVaraibles()
{
int functionStopwatch = 0;
while(true)
{
try
{
functionStopwatch = Environment.TickCount;
DataTablesMutex.WaitOne();//mutex for my shared resources
//insert into DB
}
catch (Exception ex)
{
//Handle
}
finally
{
DataTablesMutex.ReleaseMutex();
}
//simulate the timer tick value
functionStopwatch = Environment.TickCount - functionStopwatch;
int diff = INSERTION_PERIOD - functionStopwatch;
int sleep = diff >= 0 ? diff:0;
Thread.Sleep(sleep);
}
}
Follow up on Matthew Brindley's answer.
If converting code from
lock (LocalConnection.locker) {...}
then you could either do a IDisposable or do what I did:
public static void Locking(Action action) {
Lock();
try {
action();
} finally {
Unlock();
}
}
LocalConnection.Locking( () => {...});
I decided against IDisposable because it would creates a new invisible object on every call.
As to reentrancy issue I modified the code to this:
public sealed class QueuedLock {
private object innerLock = new object();
private volatile int ticketsCount = 0;
private volatile int ticketToRide = 1;
ThreadLocal<int> reenter = new ThreadLocal<int>();
public void Enter() {
reenter.Value++;
if ( reenter.Value > 1 )
return;
int myTicket = Interlocked.Increment( ref ticketsCount );
Monitor.Enter( innerLock );
while ( true ) {
if ( myTicket == ticketToRide ) {
return;
} else {
Monitor.Wait( innerLock );
}
}
}
public void Exit() {
if ( reenter.Value > 0 )
reenter.Value--;
if ( reenter.Value > 0 )
return;
Interlocked.Increment( ref ticketToRide );
Monitor.PulseAll( innerLock );
Monitor.Exit( innerLock );
}
}
In case anyone needs Matt's solution in F#
type internal QueuedLock() =
let innerLock = Object()
let ticketsCount = ref 0
let ticketToRide = ref 1
member __.Enter () =
let myTicket = Interlocked.Increment ticketsCount
Monitor.Enter innerLock
while myTicket <> Volatile.Read ticketToRide do
Monitor.Wait innerLock |> ignore
member __.Exit () =
Interlocked.Increment ticketToRide |> ignore
Monitor.PulseAll innerLock
Monitor.Exit innerLock
Elaborating on Matt Brindley's great answer so that it works with the using statement:
public sealed class QueuedLockProvider
{
private readonly object _innerLock;
private volatile int _ticketsCount = 0;
private volatile int _ticketToRide = 1;
public QueuedLockProvider()
{
_innerLock = new object();
}
public Lock GetLock()
{
return new Lock(this);
}
private void Enter()
{
int myTicket = Interlocked.Increment(ref _ticketsCount);
Monitor.Enter(_innerLock);
while (true)
{
if (myTicket == _ticketToRide)
{
return;
}
else
{
Monitor.Wait(_innerLock);
}
}
}
private void Exit()
{
Interlocked.Increment(ref _ticketToRide);
Monitor.PulseAll(_innerLock);
Monitor.Exit(_innerLock);
}
public class Lock : IDisposable
{
private readonly QueuedLockProvider _lockProvider;
internal Lock(QueuedLockProvider lockProvider)
{
_lockProvider = lockProvider;
_lockProvider.Enter();
}
public void Dispose()
{
_lockProvider.Exit();
}
}
}
Now use it like this:
QueuedLockProvider _myLockProvider = new QueuedLockProvider();
// ...
using(_myLockProvider.GetLock())
{
// here code which needs to be synchronized
// in correct order
}
NOTE: The examples provided are susceptible to Deadlocks.
Example:
QueuedLock queuedLock = new QueuedLock();
void func1()
{
try
{
queuedLock.Enter();
fubc2()
}
finally
{
queuedLock.Exit();
}
}
void func2()
{
try
{
queuedLock.Enter(); //<<<< DEADLOCK
}
finally
{
queuedLock.Exit();
}
}
Re. optional solution (inc. an optional IDisposable usage):
public sealed class QueuedLock
{
private class SyncObject : IDisposable
{
private Action m_action = null;
public SyncObject(Action action)
{
m_action = action;
}
public void Dispose()
{
lock (this)
{
var action = m_action;
m_action = null;
action?.Invoke();
}
}
}
private readonly object m_innerLock = new Object();
private volatile uint m_ticketsCount = 0;
private volatile uint m_ticketToRide = 1;
public bool Enter()
{
if (Monitor.IsEntered(m_innerLock))
return false;
uint myTicket = Interlocked.Increment(ref m_ticketsCount);
Monitor.Enter(m_innerLock);
while (true)
{
if (myTicket == m_ticketToRide)
return true;
Monitor.Wait(m_innerLock);
}
}
public void Exit()
{
Interlocked.Increment(ref m_ticketToRide);
Monitor.PulseAll(m_innerLock);
Monitor.Exit(m_innerLock);
}
public IDisposable GetLock()
{
if (Enter())
return new SyncObject(Exit);
return new SyncObject(null);
}
}
Usage:
QueuedLock queuedLock = new QueuedLock();
void func1()
{
bool isLockAquire = false;
try
{
isLockAquire = queuedLock.Enter();
// here code which needs to be synchronized in correct order
}
finally
{
if (isLockAquire)
queuedLock.Exit();
}
}
or:
QueuedLock queuedLock = new QueuedLock();
void func1()
{
using (queuedLock.GetLock())
{
// here code which needs to be synchronized in correct order
}
}