What Are the Differences Between Various Threading Synchronization Options in C#

C# threading and synchronization

Only a single thread at a time may acquire the lock, so this state is exclusive for all threads on a single lock instance. So in your example, only one method body may be executing at any given time for all instances of class Synchronization as your lock is static. If you want locking per instance of your class, then do not mark the lock object as static.

Your assumptions regarding synchronization are correct.

Please note that you should mark the lock object readonly for a completely water-tight solution. As the code stands, it would be possible for the lock object to be re-assigned and so break the locking semantics, e.g.:

public class Synchronization
{
private static object _lock = new object();

public void MethodA()
{
lock (_lock)
{
Console.WriteLine("I shouldn't execute with MethodB");
}
}

public void MethodB()
{
//This shouldn't be allowed!
_lock = new object();

lock (_lock)
{
Console.WriteLine("I shouldn't execute with MethodA");
}
}
}

Lock object should be marked as readonly, i.e.:

private static readonly object _lock = new object();

thread synchronization c#

The call of Monitor.Enter and Monitor.Exit must be done by thread themselves. In your code, this is done by the main thread in the process of setting up the two threads. Moreover, that needs to be a monitor of the same object, not two different objects like it is in your case.

To fix this, you need to move the Enter/Exit onto the thread into the delegate, and add a common object for the monitor, like this:

object mon = new object();
Thread t = new Thread(()=>{
Monitor.Enter(mon);
Met1(x);
Monitor.Exit(mon);
});
Thread t2 = new Thread(()=>{
Monitor.Enter(mon);
Met2(x,y);
Monitor.Exit(mon);
});
t.start();
t2.start();

Now the t thread would not be able to call Met1 while Met2 is going, and vice versa.

Difference between manual locking and Synchronized methods

The first method is preferred because you can (and should) make _syncRoot private. This lowers the risk of deadlocking.

The MethodImplOptions.Synchronized is a left-over from an earlier ambitious idea that turned out to be not so good after all.

Regarding the last question: Yes, according to this blog they are functionally equivalent (but not implemented the same way). And all forms of lock(this) are discouraged, again because of deadlock scenarios.

C# Synchronizing Threads depending on string

My first idea was to create a Dictionary with the key beeing the file and the value beeing the lock-object. But I was wondering if it would also be possible to lock the string file? Any thoughts on this?

If the strings will be created at runtime, locking on the string will not be safe. It would be better to make a dictionary of objects, and use those to lock.

That being said, you should consider using a ConcurrentDictionary<string,object> for this dictionary, as it will prevent a race condition (or other lock) in your dictionary itself. By using GetOrAdd you can safely get the appropriate object to use for locking.

That being said, a different approach may be appropriate here. You could use a ConcurrentQueue or BlockingCollection to provide a list of items to process, and then have a fixed number of threads process them. This will prevent synchronization problems from occurring in the first place.

C# complex thread synchronization

Use the ReaderWriterLockSlim Class:

Represents a lock that is used to manage access to a resource,
allowing multiple threads for reading or exclusive access for writing.

Use ReaderWriterLockSlim to protect a resource that is read by
multiple threads and written to by one thread at a time.
ReaderWriterLockSlim allows multiple threads to be in read mode,
allows one thread to be in write mode with exclusive ownership of the
lock, and allows one thread that has read access to be in upgradeable
read mode, from which the thread can upgrade to write mode without
having to relinquish its read access to the resource.

Joe Albahari's Threading in C# is a great resource.

Thread synchronization between UI and a worker thread

After spending most of the day trying to implement a custom SynchronizationContext, and being fairly successful in that, I stumbled into a way of making a BlockingCollection<T> work for my needs.

TL;DR for the posts which suggest using a BlockingCollection<T> to ensure Actions/delegates/methods are run on one thread, here is a boiled down (you need to add your own error handling) example:

PoorMansSynchronizier aka AutomataionActionQueue.

public class PoorMansSynchronizier
{
private readonly BlockingCollection<Action> _queue;
private readonly Thread _thread;

public PoorMansSynchronizier(Action<Thread> options)
{
_queue = new BlockingCollection<Action>();

_thread = new Thread(() => Execute());

options?.Invoke(_thread);

if (!_thread.IsAlive) _thread.Start();
}

public void Invoke(Action action) => _queue.Add(action);

public void Complete() => _queue.CompleteAdding();

private void Execute()
{
foreach (var action in _queue.GetConsumingEnumerable())
{
action.Invoke();
}
}
}

Updated Background Worker Service

public class AutomationBackgroundWorker
{
private ManualResetEvent manualResetEvent;
private PoorMansSynchronizier poorMansSynchronizier;

public AutomationBackgroundWorker()
{
Thread.CurrentThread.Name = "Automation Background Worker";

poorMansSynchronizier = new PoorMansSynchronizier(thread =>
{
thread.Name = "Automation Thread";
thread.SetApartmentState(ApartmentState.STA);
});

manualResetEvent = new ManualResetEvent(false);
}

public void Begin()
{
poorMansSynchronizier.Invoke(() => Automation());
manualResetEvent.WaitOne();
}

public void Complete()
{
manualResetEvent.Set();
manualResetEvent.Close();

poorMansSynchronizier.Complete();
}

public void Automation()
{
AutomationMessenger.Send("Status", "Initializing", null);

// Initialize Automation Elements, etc..
Task.Delay(1000).Wait();

AutomationMessenger.Send("Status", "Initialized", null);

// Get user input
AutomationMessenger.Send("UserInput", "Please Enter Your Name", UserInputCallback);

// Wait for their response
}

private void UserInputCallback(string userInput)
{
poorMansSynchronizier.Invoke(() =>
{
// Do something with the response
if (string.IsNullOrWhiteSpace(userInput)) return;

AutomationMessenger.Send("Status", $"Thanks {userInput}", null);

Complete();
});
}
}

The key here is, that the poor man's synchronizer has its own Thread, and its purpose is to execute each item on the queue.

I was able to do away with the background worker having to hold a reference to the old automationThread in the original question code, and now any action I need to run on the STA automation thread, I just pass to the queue.

This has no effect on the original implementation of the non-user-facing automation code- As far as it knows, it's still running on an STA thread named Automation Thread, and it's as happy as it was before the user-facing requirements were implemented- No doom and gloom or crippling technical debt that only a giant refactor of the automation code could solve @Enigmativity.

Proper way to synchronize a property's value in a multi-threaded application

  1. Yes, the lock (OptionsLock) ensures that all threads will see the latest value of the Options, because memory barriers are inserted when entering and exiting the lock.
  2. Replacing the lock with methods of the Interlocked or the Volatile class would serve equally well the latest-value-visibility goal. Memory barriers are inserted by these methods as well. I think that using the Volatile communicates better the intentions of the code:
public ContactOptions Options
{
get => Volatile.Read(ref _Options);
set => Volatile.Write(ref _Options, value);
}

  1. Omitting the barrier in either the get or the set accessor puts you automatically in the big black forest of memory models, cache coherency protocols and CPU architectures. In order to know if it's safe to omit it, intricate knowledge of the targeted hardware/OS configuration is required. You will need either an expert's advice, or to become an expert yourself. If you prefer to stay in the realm of software development, don't omit the barrier!


Related Topics



Leave a reply



Submit