1

How can I lock a function such that it blocks other instances of the program from executing it until it's finished?

I think I need a system mutex of some kind and a WaitOne but I'm not sure how to put it together in a robust way. I'm thinking something like...

public static void function()
{
    bool ok;

    using (Mutex mutex = new Mutex(true, "Set-IP Instance", out ok))
    {
        if(!ok)
        {                    
            mutex.WaitOne();
        }

        ...
    }
}  
Rob
  • 26,989
  • 16
  • 82
  • 98
Robinson
  • 9,666
  • 16
  • 71
  • 115
  • Possible duplicate of [Prevent multiple instances of a given app in .NET?](http://stackoverflow.com/questions/93989/prevent-multiple-instances-of-a-given-app-in-net) – Sinatr May 09 '16 at 10:05
  • Isn't a `lock` statement enough for this? Declare a dummy `object pLock = new object()` in the class and in the beginning of function wrap everything in `lock(pLock) { /* .. */ }`. (https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx). Low-Level mutex should work, too. – Maximilian Gerhardt May 09 '16 at 10:05
  • @MaximilianGerhardt No - lock will only work if he has multiple threads running, not if the same program executes multiple times – Manfred Radlwimmer May 09 '16 at 10:07
  • No, this isn't about preventing multiple instance of the same program running. It's to prevent multiple instances running the same function at the same time. The context is I'm assigning an IP address to a piece of hardware and the user can have more than one plugged in at the same time, so I need to ensure I assign unique address. I think I should lock the function that sets the IP, system-wide. – Robinson May 09 '16 at 10:07
  • *"blocks other instances of the program"* is not sufficiently clear. Do you need to block other instances from running code on the same desktop, the same user account, the same machine, or globally? – IInspectable May 09 '16 at 10:13
  • Mutex doesn't fits for this. I think you just want to prevent a function to be ran parallel in the same process/program. What about a simple var in the class of the specified method indicating if the method is used by another class? Something like `running` which is set to true at the beginning of the method and to false at the end of it? – Christian Klemm May 09 '16 at 10:15
  • "I need to ensure I assign unique address" - I can understand that. What confuses me is, what *process* are you following to generate the address such that it's unsafe for multiple programs to perform it at the same time? – Damien_The_Unbeliever May 09 '16 at 10:22
  • No, I want to wait for *any other instance* of the program to finish using the function before I use it. – Robinson May 09 '16 at 10:22
  • Damien, I'm calling GetAllNetworkInterfaces to enumerate NICs and if my device is among them I return. If it isn't, I want to assign it a unique IP address. If I have two connected, how do I assign each one a unique address? At the moment the problem is both devices appear to have the same address. – Robinson May 09 '16 at 10:28
  • You still haven't answered the question about scope. At what scope do you need to prevent multiple instances to run a section of code? Same desktop? Same user? Same machine? Globally? @chris579: The question is specifically asking for **instances** of a program, i.e. separate processes. Even if this were inside a single process, a simple variable wouldn't do. It's lacking the atomicity requirements and potentially won't reflect changes across logical/physical CPU cores. – IInspectable May 09 '16 at 10:52
  • It's instances of a program. – Robinson May 09 '16 at 11:42
  • Yes, we understood as much. But at what **scope**? Should your application, running on my machine, registered in my private network wait for another instance, running on your machine in your private network? Or do you want to prevent this on a single machine, or desktop? The answer will be different, depending on the scope you need. – IInspectable May 09 '16 at 14:53

1 Answers1

1

The behavior you are looking for can be achieved with this code (simple version):

using(Mutex mutex = new Mutex(false, "GlobalMutexId"))
{
    mutex.WaitOne();
    //Do your thing
    mutex.ReleaseMutex();
}

Or if you prefer a more reusable approach:

public class MutexFactory
{
    private string _name;

    public MutexFactory(string name)
    {
        _name = name;
    }

    public SingleUseMutex Lock()
    {
        return new SingleUseMutex(_name);
    }
}

public class SingleUseMutex : IDisposable
{
    private readonly Mutex _mutex;

    internal SingleUseMutex(string name)
    {
        _mutex = new Mutex(false, name);
        _mutex.WaitOne();
    }

    public void Dispose()
    {
        _mutex.ReleaseMutex();
        _mutex.Dispose();
    }
}

Can be used like this:

private void TestFunction()
{
    MutexFactory factory = new MutexFactory("YourMutexId");

    for (int i = 0; i < 100; i++)
    {
        // also works for new instances of your program of course
        new Thread(Interlocked).Start(factory);
    }
}

private void Interlocked(object obj)
{
    Guid guid = Guid.NewGuid();
    MutexFactory factory = obj as MutexFactory;
    using (factory.Lock())
    {
        Debug.WriteLine(guid.ToString("N") + " start");
        //Waste Time
        Thread.Sleep(50);
        Debug.WriteLine(guid.ToString("N") + " end");   
    }
}
Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62