C# Mutex - Timeout - networked applications

Markus Freitag 3,791 Reputation points
2021-12-02T14:53:01.593+00:00

Hello,

I am looking at an example of how to solve the following requirement.
154522--app-file.png

I have three PCs that are networked.
On each of them the application XY is running.
This increases the counter. No number may occur twice.

How can I solve this?
Problems
If an application cannot read within 3 seconds, error message.
The WaitOne does not work, does not wait.
How can I check, if it works?

My attempts.

private void IncreaseTheCounter()  
        {  
  
            XmlSerializer serialiser = new XmlSerializer(typeof(EANCODING));  
            using (StreamReader reader = new StreamReader(filepath))  
            {  
                obj = (EANCODING)serialiser.Deserialize(reader);  
  
                // Increment the current counter  
                obj.CURRENTCOUNTER.value++;  
  
                Console.WriteLine($"New counter: {obj.CURRENTCOUNTER.value}");  
            }  
  
            // Serialise the obj back to XML and write it back to the file.  
            using (StreamWriter writer = new StreamWriter(filepath))  
            {  
                using (var xmlWriter = XmlWriter.Create(writer, new XmlWriterSettings { Indent = true }))  
                    serialiser.Serialize(xmlWriter, obj);  
            }  
        }  
  
  
        private void btnTestMutex_Click(object sender, EventArgs e)  
        {  
            Boolean isMyFileFree;  
            var hasHandle = false;  
  
            using (Mutex mutex = new Mutex(true, "Mutex4MyFile", out isMyFileFree))  
            {  
                try  
                {  
                    hasHandle = mutex.WaitOne(3000, false);  
  
                    IncreaseTheCounter();     
  
  
                    if (hasHandle == false)  
                        throw new TimeoutException("Timeout waiting for exclusive access");  
                }  
                catch (AbandonedMutexException)  
                {  
                    // maybe the mutex was abandoned in another process, it will still get aquired  
                    hasHandle = true;  
                }  
                finally  
                {  
                    if (hasHandle)  
                        mutex.ReleaseMutex();  
                }  
            }  
  
        }  


<?xml version="1.0" encoding="utf-8"?>  
<EANCODING xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
  <CURRENTCOUNTER value="1230000023133" />  
  <RESET value="Daily" />  
  <LASTMODIFY value="Application3" />  
  <DATETIME value="02.12.2021 15:52" />  
  <STEP value="1" />  
</EANCODING>  
Developer technologies C#
0 comments No comments
{count} votes

5 answers

Sort by: Most helpful
  1. RLWA32 49,536 Reputation points
    2021-12-04T14:45:06.647+00:00

    Following sample uses a mutex for multiple processes on the same system -

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    
    namespace CSMarkusMutex
    {
        class Program
        {
            static void Main(string[] args)
            {
                bool isInitialOwner;
    
                using (Mutex mutex = new Mutex(false, "MyUniqueIdApp03122021", out isInitialOwner))
                {
                    bool hasHandle = false;
                    bool bContinue = true;
                    Console.WriteLine("Initial owner is {0}", isInitialOwner);
    
                    while (bContinue)
                    {
                        try
                        {
                            Console.WriteLine("Acquiring Mutex");
                            hasHandle = mutex.WaitOne(3000);
    
                            if (hasHandle)
                            {
                                Console.WriteLine("I Own the Mutex");
                                Console.WriteLine("Continue? Y/N");
                                if(Console.ReadKey().Key == ConsoleKey.N)
                                {
                                    bContinue = false;
                                }
                                Console.Write('\n');
                            }
                            else 
                            {
                                Console.WriteLine("Another app instance is running.");
                            }
    
                        }
                        catch (AbandonedMutexException e)
                        {
                            hasHandle = true;
                            Console.WriteLine("AbandomedMutexException : {0}", e.Message);
                            Console.WriteLine("We shouldn't be here.  Protected resource could be corrupted!");
                        }
                        finally
                        {
                            if (hasHandle)
                            {
                                mutex.ReleaseMutex();
                                hasHandle = false;
                            }
                        }
    
                    } 
                }
            }
        }
    }
    
    1 person found this answer helpful.

  2. Bruce (SqlWork.com) 77,686 Reputation points Volunteer Moderator
    2021-12-04T17:20:29.59+00:00

    Using a windows Mutex requires all the processes be on the same pc. This does not match your use case. Implementing Mutexs for multiple machines is done via cluster logic, typically via a distributed lock manager.

    1 person found this answer helpful.

  3. Petrus 【KIM】 546 Reputation points
    2021-12-07T07:05:32.237+00:00

    I Think you can use the NO-SQL like Redis, Berkeley DB.

    You can share data in No-SQL over the network.

    Synchronization objects are not a good choice.

    1 person found this answer helpful.

  4. Bruce (SqlWork.com) 77,686 Reputation points Volunteer Moderator
    2021-12-07T17:33:31.197+00:00

    the easiest solution for you is to write a webservice that runs on one of the machines. all the machines call the web service to get the next counter value.

    var builder = WebApplication.CreateBuilder(args); 
    var app = builder.Build(); 
    var fileName = "counter.txt"; 
    var counter = 0; 
    
    if (!File.Exists(fileName)) 
        File.Create(fileName).Close(); 
    else 
        counter = int.Parse(File.ReadAllText(fileName)); 
    
    app.MapGet("/", () =>  
    { 
        lock(app)
        {
            ++counter; 
            File.WriteAllText(fileName,counter.ToString()); 
        }
        return counter; 
    }); 
    
    app.Run(); 
    
    1 person found this answer helpful.

  5. Bruce (SqlWork.com) 77,686 Reputation points Volunteer Moderator
    2021-12-02T16:01:11.22+00:00

    The easiest implementation is to use a database.

    For shared memory, you typically implement a distributed lock manager. These typically use a quorum of member to manage locks.

    The easiest to implement is the master host. One host manages the data. To update data, the first step is to determine the master. You ask all hosts, and use the one with the most votes (quorum). If no master is found, you do a failover and pick a new master. But you can implement all peers.

    A quorum requires an odd number of members, so if there will be a even number member system, you need a tie breaker.

    This is an advanced algorithm to implement, but fun. Should only takes a couple weeks or months.. Deadlock detection and picking a master is the most complex. Also using network broadcasting is more efficient.

    You can find articles and book on implementing a distributed lock manager.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.