这是一个在C#中创建线程安全类的好设计吗?

Joe*_*e H 12 .net c# multithreading thread-safety

通常,当我想要一个线程安全的类时,我会执行以下操作:

public class ThreadSafeClass
{
    private readonly object theLock = new object();

    private double propertyA;
    public double PropertyA
    {
        get
        {
            lock (theLock)
            {
                return propertyA;
            }
        }
        set
        {
            lock (theLock)
            {
                propertyA = value;
            }
        }
    }

    private double propertyB;
    public double PropertyB
    {
        get
        {
            lock (theLock)
            {
                return propertyB;
            }
        }
        set
        {
            lock (theLock)
            {
                propertyB = value;
            }
        }
    }

    public void SomeMethod()
    {
        lock (theLock)
        {
            PropertyA = 2.0 * PropertyB;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它有效,但它非常冗长.有时我甚至为每个方法和属性创建一个锁定对象,从而产生更多的冗长和复杂性.

我知道也可以使用Synchronization属性锁定类,但我不确定这些扩展程度如何 - 正如我经常期望的那样,有数十万甚至数百万个线程安全对象实例.这种方法会为类的每个实例创建一个同步上下文,并且需要从ContextBoundObject派生类,因此不能从其他任何东西派生 - 因为C#不允许多重继承 - 这是一个show stopper在很多情况下.

编辑:正如几位响应者所强调的那样,没有"银弹"线程安全的类设计.我只是想了解我使用的模式是否是一个很好的解决方案.当然,在任何特定情况下,最佳解决方案都取决于问题.以下几个答案包含应考虑的替代设计.

编辑:此外,线程安全有多个定义.例如,在我上面的实现中,以下代码不是线程安全的:

var myObject = new ThreadSafeClass();
myObject.PropertyA++; // NOT thread-safe
Run Code Online (Sandbox Code Playgroud)

那么,上面的类定义是否代表了一种好的方法呢?如果没有,那么对于具有类似行为的设计,您会推荐什么?这种设计对于类似的用途是线程安全的?

Cha*_*ion 8

多线程问题没有"一刀切"的解决方案.做一些关于创建不可变类的研究并了解不同的同步原语.

这是一个半不可变程序员不可变类的例子.

public class ThreadSafeClass
{
    public double A { get; private set; }
    public double B { get; private set; }
    public double C { get; private set; }

    public ThreadSafeClass(double a, double b, double c)
    {
        A = a;
        B = b;
        C = c;
    }

    public ThreadSafeClass RecalculateA()
    {
        return new ThreadSafeClass(2.0 * B, B, C);
    }
}
Run Code Online (Sandbox Code Playgroud)

此示例将同步代码移动到另一个类并序列化对实例的访问.实际上,在任何给定时间,您都不希望在对象上运行多个线程.

public class ThreadSafeClass
{
    public double PropertyA { get; set; }
    public double PropertyB { get; set; }
    public double PropertyC { get; set; }

    private ThreadSafeClass()
    {

    }

    public void ModifyClass()
    {
        // do stuff
    }

    public class Synchronizer
    {
        private ThreadSafeClass instance = new ThreadSafeClass();
        private readonly object locker = new object();

        public void Execute(Action<ThreadSafeClass> action)
        {
            lock (locker)
            {
                action(instance);
            }
        }

        public T Execute<T>(Func<ThreadSafeClass, T> func)
        {
            lock (locker)
            {
                return func(instance);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个如何使用它的快速示例.它可能看起来有点笨重,但它允许您一次执行实例上的许多操作.

var syn = new ThreadSafeClass.Synchronizer();

syn.Execute(inst => { 
    inst.PropertyA = 2.0;
    inst.PropertyB = 2.0;
    inst.PropertyC = 2.0;
});

var a = syn.Execute<double>(inst => {
    return inst.PropertyA + inst.PropertyB;
});
Run Code Online (Sandbox Code Playgroud)