Wrap a function around other functions

Viewed 75

I have a class with quite a lot of public functions. I want to prevent the situation that any of the function is executed when another function is already running. So I included a semaphore in every function like this:

public bool FunctionX(int a)
{
    if (semaphore.Wait(0))
    {
        try
        {
            return DoSomething(a);
        }
        finally
        {
            semaphore.Release();
        }
    }
    else
        return false;
}

This works, but is there a more elegant way, where I do not have to write this wrapping semaphore stuff each time? I am thinking about something of the form

public bool FunctionX(int a)
{
    WrapSemaphoreAround(DoSomething(a))
}

Note that the signature of every function is different, not necessarily bool as return value and int as parameter, so it also has to work with

public string FunctionY(byte a, bool b)
{
    WrapSemaphoreAround(DoAnotherThing(a, b))
}

Is this even possible in C#?

1 Answers

If you want to surround a method with something else you can use a delegate:

public bool DoX() => DoPreparationAndCleanup(DoXImpl);
private bool DoXImpl(){
 ...
}
private T DoPreparationAndCleanup<T>(Func<T> a){
    // do preparation
    var result = a();
    // do cleanup
    return result;
}

However, using a semaphore in that way is probably not a great idea. If some method is called by multiple threads, one or the other will fail, and what are they supposed to do in that case? Try again some time later? Just fail? In c#, a semaphore is a specialized thread safety primitive, and should be reserved for specialized use cases.

If you want to block a UI from doing anything else while the operation is in progress, my go to method is a Modal Dialog, i.e. using wpf:

var myDialog = new MyWindow();
var task = Task.Run(MySlowMethod);
task.ContinueWith( t => Dispatcher.BeginInvoke((Action)myDialog.Close));
myDialog.ShowDialog();

This will run MySlowMethod on a background thread, when this is done it will send a message to the UI thread to close the dialog. While it is running only the controls inside MyWindow can be used.

The standard primitive to ensure thread safety is a lock, that will block execution in a critical section to ensure only a single thread hold the lock at any one time. All other threads will block until the lock becomes available. This should give a more predicable program behavior, but do introduce the possibility of deadlocks.

private object myLockObject = new object();
public bool DoX(int a)
{
    lock(myLockObject){
       return DoXImpl(a);
    }
}

Ofc, even better than locks is ensuring your methods are thread safe by default, for example by using immutability and pure functions.

Another way to ensure exclusive access to some resource is with a limitedconcurrencyTaskSceduler (see example). This allow you to start multiple tasks, where only one is allowed to run at any one time.

Related