一次将锁码部分锁定到一个入口

时间:2012-12-05 00:25:01

标签: c# multithreading synchronization locking

我试图限制对单一对象的访问,因此只有一个线程 在时间使用它,此外,我想防止同一个线程访问两次 受限制的代码。

我尝试了Lock方法,我发现它的dos不会锁定锁定她的线程,只能锁定其他线程..

如下:

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");
        }
    }
}

控制台中的结果例如是:

。 。

锁定

解锁

锁定

锁定

解锁

。 。

正如我们所看到的,2个锁定评论一个接一个显示

所以它的意思是“DoSomeWork()”由计时器线程访问两次。

任何人都知道如何使这个锁定工作?

其他同步方法maby?

感谢名单。

3 个答案:

答案 0 :(得分:4)

你没有正确地进行锁定(最重要的是你正在锁定一个很大的禁令)。为了节省时间,请阅读Jon Skeet撰写的this文章并实施其中一种模式以避免让您头疼。

答案 1 :(得分:1)

在您的代码中

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

想一想。 if (_instance == null)你做lock (_instance)。所以你使用null锁定。这根本不好。

在MSDN lock Statement (C# Reference)中,如何使用lock的给定示例是:

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;
        }
    }
}

我想你应该遵循它并有一个单独的对象将它用作锁。


其次,thread syncronization原语用于分隔对不同线程的共享资源的访问。如果需要从一个线程分离访问权限,则只需使用标志。像这样:

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

在这里你应该明白,你只需跳过“由旗帜锁定”的代码。相反,如果你想让线程等待自己,特别是在多线程应用程序中,我想它看起来应该重新设计。

答案 2 :(得分:0)

在.NET中实现单例的最简单方法是:

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().
    }
}

阅读布莱恩在答案中提出的关于Singleton Pattern implementations in .NET的优秀文章。上述实现基于本文中描述的第四个版本。 CLR保证静态字段的构造将是线程安全的,因此您不需要在那里锁定。但是,如果对象具有可以更改的状态(字段),则需要锁定。

请注意,有一个private readonly object用于确保DoWork方法的互斥。这样,单个线程一次可以调用DoWork。另请注意,由于线程按顺序执行指令,因此同一线程无法同时调用此方法两次。从单个线程调用此方法两次的唯一方法是在DoWork内调用另一个最终调用DoWork的方法。我无法看到这样做的重点,如果你这样做,那么请注意避免堆栈溢出。您可以按照Konstantin的建议并使用标志,但恕我直言,你应该重新设计DoWork只做一件事,避免这样的情况。