简体   繁体   中英

Lock code section to one entrance at a time

I am trying to restrict access to an singletone object so only one thread use it at time, Furthermore, I want to prevent from the same thread accessing twice to the restricted code.

I tried the Lock method and i found out that its dosn't lock the thread that locked her, but only other threads..

as below:

public sealed class Singleton
{
    private static readonly Singleton instance    = new Singleton();

    static Singleton()
    {
    }

    private Singleton()
    {
    }

    public static Singleton Instance
    { 
        get
        {
            return instance;
        }
    }
}

public class SomeWorker
{
    private readonly Timer _doWorkTimer = new Timer(20);

    public SomeWorker()
    {
        InitiateTimer();
    }

    private void InitiateTimer()
    { 
        _doWorkTimer .Elapsed += DoWorkElapse;
        _doWorkTimer .Enabled = true;
    }

    private void DoWorkElapse(object source, ElapsedEventArgs e)
    { 
        DoSomeWork();
    }

    private void DoSomeWork()
    {
        // I know that lock on string is wrong!
        // Its just for the example only I
        // Its just to make sure all the program is use the same lock..
        lock ("ConnectionLock")
        { 
             Console.WriteLine("Lock");

             var inst = Singletone.Instance;

             // Do Some Work on "inst" ...

             Console.WriteLine("Unlock");
        }
    }
}

The result in the console for example is:

. . .

Lock

Unlock

Lock

Lock

Unlock

. . .

As we can see, 2 Lock comments shows one after another

So its mean that the "DoSomeWork()" accessed twice by the timer thread.

Anyone have any idea how to make this lock work?

Other Sync methods maby?

thanx.

You aren't doing your locking properly (and to top it off you are taking a lock on a string which is a big no-no). To save time, please read this article from Jon Skeet and implement one of the patterns to save yourself a headache.

In your code you have

public static Singletone Instance()
{
    if (_instance == null)
    {
        lock (_instance)
        {
            if (_instance == null)
            {
                _instance = new Singletone ();
            }
        }
    }
    return _instance;;
}

Think about it. if (_instance == null) you do lock (_instance) . So you lock using null . That's not good at all.

In MSDN lock Statement (C# Reference) the given example of how to use lock is:

class Account
{
    decimal balance;
    private Object thisLock = new Object();

    public void Withdraw(decimal amount)
    {
        lock (thisLock)
        {
            if (amount > balance)
            {
                throw new Exception("Insufficient funds");
            }
            balance -= amount;
        }
    }
}

I guess you should follow it and have a separate object to use it as a lock.


And secondly, thread syncronization primitives are used to separate access to shared resources for different threads. If you need to separate access from one thread, you simply need to use flags. Something like this:

bool isBusy = false;
public static void Foo()
{
    if (!isBusy)
    {
        isBusy = true;
        try
        {            
            //do the job
        }
        finally
        {
            isBusy = false;
        }
    }
}

Here you should understand that you simply skip the "locked-by-flag" code. On the contrary if you want to make the thread wait for itself, especially in a multithreading application, I guess it looks like it should be redesigned.

The easiest way to implement a singleton in .NET is:

public class Singleton : IDisposable
{
    private readonly static Singleton _instance = new Singleton();
    private readonly static object lockObject = new object();

    static Singleton()
    {
    }

    private Singleton()
    {
        InitiateConnection();
    }

    public static Singleton Instance
    {
        get { return _instance; }
    }

    /// <summary>
    /// Method that accesses the DB.
    /// </summary>
    public void DoWork()
    {
        lock (lockObject)
        {
            //Do Db work here. Only one thread can execute these commands at a time.                
        }
    }        

    ~Singleton()
    {
        //Close the connection to DB.

        //You don't want to make your singleton class implement IDisposable because
        //you don't want to allow a call to Singleton.Instance.Dispose().
    }
}

Read the excellent article on Singleton Pattern implementations in .NET that Bryan suggested in his answer. The above implementation is based on the fourth version described in the article. The CLR guarantees that the construction of the static field will thread-safe hence you do not need locking there. However you will need locking if your object has state (fields) that can be changed.

Note that there is a private readonly object used for ensuring mutual exclusion on the DoWork method. This way a single thread can call DoWork at a time. Also note that there is no way that the same thread can call this method twice at the same time since a thread executes instructions sequentially. The only way this method could be called twice from a single thread is if inside DoWork you call another method that eventually calls DoWork . I can't see the point of doing this and if you do then take care to avoid stack overflows. You could follow the suggestion of Konstantin and use a flag but IMHO you should redesign DoWork to do just one thing and avoid scenarios like these.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM