String Interpolation with Variable Content in C#

SynchronizationLockException on Monitor.Exit when using await

You can't await a task inside a lock scope (which is syntactic sugar for Monitor.Enter and Monitor.Exit). Using a Monitor directly will fool the compiler but not the framework.

async-await has no thread-affinity like a Monitor does. The code after the await will probably run in a different thread than the code before it. Which means that the thread that releases the Monitor isn't necessarily the one that acquired it.

Either don't use async-await in this case, or use a different synchronization construct like SemaphoreSlim or an AsyncLock you can build yourself. Here's mine: https://stackoverflow.com/a/21011273/885318

why monitor.exit does not work in another work?

Monitor has thread-affinity. You can't exit it from a different thread.

But it's not clear what you're trying to do here anyway. You're already in an async method, calling another async method. Just use await - just setup an synchronization context to allow you to use await properly in a console application. Or, use Task.Wait if you're not worried about deadlocks :)

As for Monitor.Wait, it doesn't do what you think it does. The wait is for a signal, not for the monitor being exited. For example:

static readonly object syncObject = new object();

void Thread1()
{
lock (syncObject) Monitor.Wait(syncObject);
}

void Thread2()
{
lock (syncObject) Monitor.Pulse(syncObject);
}

Both methods are executed on separate threads in this scenario. If Thread1 runs first, it will take the lock, wait for a signal (this exits the lock until the signal is presented) and reacquire the lock after the signal is given. Thread1 provides the signal using the Monitor.Pulse method. Note that in both cases, the lock is taken on a single thread, and exited on the same thread.

This mechanism isn't very easy to use properly, and is somewhat slow, so you're not going to see it much.

Also, the threads used by Task.Run aren't yours. It's usually undesirable to use blocking code on thread-pool threads - make sure you understand the trade-offs your making. Even more importantly, tasks have no thread-affinity - so using a thread-affine synchronization primitive like a monitor is rather adventurous :) In your case, even if you took the lock inside the Task.Run rather than the outside, it would be possible for the Monitor.Exit to fail, since you might have gotten a different thread after the await.

Don't forget that the trickiest part about multi-threading isn't that it just doesn't work - it's that it has an ugly tendency to mostly work for the most part, and fail in weird scenarios (that happen all the time in practice, mind you). Testing isn't quite enough to give you much confidence in how a multi-threaded piece of code behaves in practice. All you get is tons of tiny, almost impossible to reproduce bugs. You got lucky here - your code fails reliably; that certainly isn't the case with most multi-threading issues :)

Joe Albahari's introduction to threading is a huge help for multi-threaded programming of any kind. I highly recommend reading through the whole of it at least once. Keep it in your bookmarks for future reference :)

SynchronizationLockException after await

As you've noticed, the Monitor.Enter/Exit are thread-affine.

In their series on creating async-friendly coordination primitives, Stephen includes an AsyncLock that should be what you're looking for AFAICT.

Exception on Monitor.Exit in C#

It should be very clear why you are getting the exception in your new code. If the lock is taken then the object that is unlocked is not the object that was locked. Locks take an object, not a variable. The correct translation of the deeply wrong original code is

// THIS CODE IS COMPLETELY WRONG; DO NOT USE IT
if (_myVar != null)
{
bool lockTaken = false;
var locker = _myVar;
try
{
Monitor.TryEnter(locker, new TimeSpan(0, 0, 5), ref lockTaken);
if (lockTaken)
{
_myVar = newMyVar; // where newMyVar is another List<myClass>
}
}
finally
{
if (lockTaken) Monitor.Exit(locker);
}
}

Which will not throw on exit, but is still completely wrong.

Never lock on the contents of a variable and then mutate the variable; every subsequent lock will lock on a different object! So you have no mutual exclusion.

And never lock on a public object! If that list leaks out anywhere then other wrong code can be locking on that list in an unexpected order, which means deadlocks -- which is the original symptom you are diagnosing.

The correct practice for locking on a field is to create a private readonly object field used only as a locker, and used every time the field is accessed. That way you know that (1) the field is always accessed under the same lock, no matter its value, and (2) the lock object is used only for locking that field, and not for locking something else. That ensures mutual exclusion and prevents deadlocks.

The fact that someone wrote a large multithreaded program without understanding the most basic facts about locks means that it is almost certainly a complete mess of hard-to-find bugs. The fact that this wasn't immediately obvious upon reading the code means that you don't have enough knowledge of threading to fix the problems correctly. You're going to need to either find an expert on this stuff who can help you, or gain at least a minimal working knowledge of correct practices.

I cannot emphasize enough that this is hard stuff. Programs with multiple threads of control in them are extremely difficult to write correctly on modern hardware; many of the things you believe are guaranteed by the language are only guaranteed in single threaded programs.

Monitor.TryEnter / Monitor.Exit and SynchronizationLockException

As you think that to put the calling of Monitor.Exit in try-catch was 'durty'(dirty?), here's a very simple idea trying to 'take the durty away'. Lock is reentrant for the same thread and if one thread acquired successfully, before it releases, attempt from another thread will fail. So that you can consider something like:

public void Exit(object key) {
if(!IsActive) {
return;
}

if(LockDictionary.ContainsKey(key)) {
var syncObject=LockDictionary[key];

if(Monitor.TryEnter(syncObject.SyncObject, 0)) {
SetLockExit(syncObject);
Monitor.Exit(syncObject.SyncObject);
Monitor.Exit(syncObject.SyncObject);
}
}
}

We call Monitor.Exit twice because we lock it twice, one in the code outer, and one just here.

SynchronizationLockException: Cannot wait on monitors on this runtime. - Workaround for TaskCompletionSource in Blazor wasm

I've inspected code by the github link you provided and noticed that you are doing this:

_stringReaderRedirect = new StringReaderRedirect(Read);

where Read is function mentioned in question. Then inside StringReaderRedirect you have:

private readonly Func<Task<string>> _ReadRedirectFunc;
public StringReaderRedirect(Func<Task<string>> readredirect) : base("foo")
{
_ReadRedirectFunc = readredirect;
}

And then you do this:

public override string ReadLine()
{
//return _ReadRedirectFunc?.Invoke();
//return base.ReadLine();
Task<string> task = _ReadRedirectFunc?.Invoke();

return task?.GetAwaiter().GetResult();
}

So you are blocking on asynchronous call, which is the source of exception in question. Doing this is a major no-no in a single threaded environment, like Blazor WASM. If exception you see were not thrown then you would have a deadlock: the only thread (UI) is blocked waiting for the result of Read, while Read itself depends on user input, for which UI thread is required. There are many similar issues on blazor github repo, for example.

The same would happen in WPF by the way, if you did Read().GetAwaiter().GetResult() from UI thread. Well not the same, because in case of WPF it would just deadlock, but "will not work" also.

So go async all the way and never block main thread, since it's the only thread you have.

Object synchronization exception only when using breakpoints, in F#

This error throws when you try to exit a lock object you never entered.

You have to write Monitor.Exit(UpdateLock) when you definetely entered the Monitor.TryEnter(UpdateLock).

This can be achieved when you turn the exit into the if like:

if Monitor.TryEnter(UpdateLock) then
try
try
let response = RestClient.Execute(Request)
response |> ignore
with ex ->
Logging.Error(printfn "%s" ex.Message)
finally
Monitor.Exit(UpdateLock)

I hope this helps.

Appropriate use of Monitor.Exit

Since, someCondition is not passed as a parameter, I could only assume the someCondition could be changing at any time (possibly an instance variable to the class). So, I would write the code this way:

void MethodA()
{
if (someCondition)
{
bool conditionReached = false;

Monitor.Enter(this);
try
{
if (someCondition)
{
conditionReached = true;
}
}
finally
{
Monitor.Exit(this);
}

if (conditionReached)
{
MethodB();
}
}
}

If not, than the previous answer with condition declared locally would suit you.



Related Topics



Leave a reply



Submit