1

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

我尝试了 Lock 方法,我发现它并没有锁定锁定她的线程,而只是锁定其他线程..

如下:

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条Lock评论一个接一个显示

所以这意味着计时器线程访问了两次“DoSomeWork()”。

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

其他同步方法可能吗?

谢谢。

4

3 回答 3

4

您没有正确锁定(最重要的是,您正在锁定一根绳子,这是一个很大的禁忌)。为了节省时间,请阅读Jon Skeet 的这篇文章,并实现其中一种模式,以免让自己头疼。

于 2012-12-05T00:30:57.383 回答
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;
        }
    }
}

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


其次,线程同步原语用于分离不同线程对共享资源的访问。如果你需要从一个线程中分离访问,你只需要使用标志。像这样的东西:

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

在这里你应该明白你只是跳过了“locked-by-flag”代码。相反,如果你想让线程等待自己,特别是在多线程应用程序中,我想它看起来应该重新设计。

于 2012-12-05T00:41:45.257 回答
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().
    }
}

阅读Bryan 在他的回答中建议的关于.NET中的单例模式实现的优秀文章。上面的实现是基于文章中描述的第四个版本。CLR 保证静态字段的构造是线程安全的,因此您不需要在那里锁定。但是,如果您的对象具有可以更改的状态(字段),您将需要锁定。

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

于 2012-12-05T13:45:41.667 回答