我应该在以下情况下使用锁吗?

本文关键字:情况下 我应该 | 更新日期: 2023-09-27 17:56:32

我设计了一个Stack包装类。我的困惑是,我应该在弹出或将对象推送到堆栈变量" ParameterStack "时使用lock。请让我知道,这个类是否线程安全。

public static class StackManager
{
    private static Stack ParameterStack = new Stack();
    public static T Pop<T>()
    {
        T RawObject;
        T Result = default(T);
        lock (ParameterStack)
        {
            RawObject = (T)ParameterStack.Pop();
        }
        if (RawObject != null && RawObject is T)
            Result = (T)RawObject;
        return (T)Result;
    }
    public static void Push<T>(T Data)
    {
        lock (ParameterStack)
        {
            ParameterStack.Push(Data);
        }
    }
}

我创建了这个StackManager类是为了学习目的。

我应该在以下情况下使用锁吗?

看起来还行。有一个(相当理论性的)论点是,锁定ParameterStack本身并不完全安全,因为你不拥有代码。假设堆栈内部的某个地方做了一个lock(this),你可能会死锁。

public static class StackManager
{
    private static Stack parameterStack = new Stack();
    private static object stackLock = new object();
    // now use lock(stackLock) instead of lock(ParameterStack)
}

是的,你应该这样做。默认情况下,堆栈在 C# 中不同步。

是的,它是线程安全的。只要内部堆栈的任何使用都在锁内,就可以安全地与多个线程一起使用。

顺便说一下,您可以使用Stack<T>来避免所有转换。或者正如 oxilumin 所说,如果您不只是想学习如何制作线程安全的东西,请使用 ConcurrentStack。

您应该在情况下使用 ConcurrentStack。如果您无法使用ConcurrentStack - 您可以使用Stack.Synchronized()方法:

Stack mySynchronizedStack = Stack.Synchronized(myStack);

但是,即使您使用Synchronized()方法枚举仍然不是线程安全的,也应该使用锁来枚举堆栈。

Stack myStack = new Stack();
lock (myStack.SyncRoot)
{
    foreach (var element in myStack)
    {
    }
}

不幸的是,Stack的通用版本没有Synchonization()方法。因此,您的代码应该是:

public static class StackManager
{
    private static Stack ParameterStack;
    static StackManager()
    {
        ParameterStack = Stack.Synchronized(new Stack());
    }
    public static T Pop<T>()
    {
        object RawObject = ParameterStack.Pop();
        return RawObject is T ? (T)RawObject : default(T);
    }
    public static void Push<T>(T Data)
    {
        ParameterStack.Push(Data);
    }
}
如果要

检查类型,还应使用object类型进行RawObject。在代码中,如果尝试Pop不同类型的对象,则会出现异常。

你知道 ConcurrentStack 类吗? 它是一种使用无锁的高效线程安全实现