You must defeat the GC and destroy the object when it goes out of scope.

I have several sections of code that I need to protect with Mutex. The problem is that the code looks something like this:

lock(mylockobject) { if(!foo()) throw new MyException("foo failed"); if(!bar()) throw new MyException("bar failed"); } 

Using lock works as I would like, but now I need to use a mutex. The obvious problem here is that if I get mutex and foo () or bar (), then I have to do the mutex exclusion before throwing each exception.

In C ++, I would take advantage of the amount of the object created on the stack and lock the mutex in the constructor of the object, and then release it in the destructor. With .NET garbage collection, I did not think this would work. I wrote a test application and confirmed that if I do something like this:

 public class AutoMutex { private Mutex _mutex; public AutoMutex(Mutex mutex) { _mutex = mutex; _mutex.WaitOne(); } ~AutoMutex() { _mutex.ReleaseMutex(); } } 

and then enter the code as follows:

 // some code here... Mutex my_mutex = new Mutex(false, "MyMutex"); { // scoping like I would do in C++ AutoMutex test = new AutoMutex(my_mutex); test = null; } 

A destructor (finalizer?) Is not called until much later.

Google hasn't pointed me in the right direction yet, but I'm still working on it ... Please let me know how you can solve this little problem, if possible.

+4
source share
8 answers

To provide an overview, you can make AutoMutex implement IDisposable and use it as follows:

 using(new AutoMutex(.....)) { if(!foo()) throw new MyException("foo failed"); if(!bar()) throw new MyException("bar failed"); } 

In your implementation of IDisposable.Dispose() release the mutex.

+12
source

A pair of glasses.

1) What you want to find is a “one-time template”. Be very careful to implement it correctly. Of course, Mutex already implements a one-time template, so it’s not clear why you want to make your own, but it's good to know it.

Look at this question for some additional thoughts on the feasibility of using a one-time template, as if it were RAII:

Is it insulting to use IDisposable and "using" as a means to get "scope behavior". for exception safety?

2) Try-finally also has the semantics you want. Of course, the “used” block is just syntactic sugar for try-finally.

3) Are you sure you want to let go of the mutex when it throws something? Are you sure you want to drop into a protected region?

This is a bad code smell for the following reason.

Why do you have a mutex in the first place? Usually, since the pattern is as follows:

  • condition agreed but outdated
  • block access to state
  • make a condition mismatch
  • make state consistent
  • share status
  • now consistent and new

Think about what happens when you throw an exception before you make the state consistent. You open access to a state that is now incompatible and outdated .

It might be better to keep the castle. Yes, this means a risk of deadlocks, but at least your program does not work on garbage, an outdated, inconsistent state.

This is a terrible, terrible thing that throws an exception from within a blocking region, and you should avoid this whenever possible. The exception thrown inside the lock forces you to choose between two terrible things: either you get deadlocks, or you get crazy crashes and irreproducible behavior when your program manages an inconsistent state.

The pattern you really have to follow:

  • condition agreed but outdated
  • block access to state
  • make a condition mismatch
  • make state consistent
  • if an exception occurs, rollback to an obsolete, consistent state
  • share status
  • now agreed and, if there were no exceptions, fresh

This is a much safer alternative, but writing code that does such transactions is tough. No one said multithreading was easy.

+16
source

Mutex implements IDisposable, so wrap it in using

 using (Mutex m = new Mutex()) { //Use mutex here } //Mutex is out of scope and disposed 
+9
source

Use the try / finally block or use the IDisposable template and wrap your usage in the using statement.

+7
source

I think all of you have the ability to use / use, but here is an example using try / finally:

 Mutex m = new Mutex() m.WaitOne(); try { if(..) throw new Exception(); } finally { // code in the finally will run regardless of whether and exception is thrown. m.ReleaseMutex(); }
Mutex m = new Mutex() m.WaitOne(); try { if(..) throw new Exception(); } finally { // code in the finally will run regardless of whether and exception is thrown. m.ReleaseMutex(); } 
+3
source

GC is not deterministic. In addition, it behaves differently in debug mode, which makes it more confusing to work in a development environment.

To create and use your automatic mutexes as you wish, implement IDisposable and use the using keyword to destroy / release AutoMutex when it is out of scope.

 public class AutoMutex : IDisposable { private Mutex _mutex; public AutoMutex(Mutex mutex) { _mutex = mutex; _mutex.WaitOne(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } void Dispose(bool disposing) { // method may be called more than once from different threads // you should avoid exceptions in the Dispose method var victim = _mutex; _mutex = null; if(victim != null) { victim.ReleaseMutex(); victim.Dispose(); } if(disposing) { // release managed resources here } } } 

and when using

 using(new AutoMutex(new Mutex(false, "MyMutex"))) { // whatever within the scope of the mutex here } 
+3
source

Why don't you surround the code in Foo () with try-catch-finally, and then finally release the mutex? (The trick can reconstruct any exceptions, packing them into its own custom exception type if necessary.)

+1
source

For scope based action, the C # try...finally idiom. It will look like this:

 try { acquire the mutex do your stuff which may fail with an exception } finally { release the mutex } 

Using object instances to collect resources is C ++ - ism, which is based on the ability of C ++ to process instances with a resource limited to the current scope. In languages ​​where instances are heaped and destroyed asynchronously by the garbage collector (C #, Java), destructors (like finalizers) are not a suitable tool for this. Instead, the finally construct is used to perform an area-based action.

0
source

Source: https://habr.com/ru/post/1300313/


All Articles