多线程线程安全读/写锁定的最佳C#解决方案?

Ale*_*lex 4 .net c# multithreading

static在C#中对多线程环境中的成员执行锁定读/写访问的最安全(和最短)方法是什么?

是否可以在类级别上执行线程安全锁定和解锁(因此,每次需要静态成员访问时,我都不会继续重复锁定/解锁代码)?

编辑:示例代码会很棒:)

编辑:我应该使用volatile关键字或Thread.MemoryBarrier()来避免多处理器缓存还是不必要?根据Jon Skeet的说法,只有那些会让其他处理器看到变化吗?(另被问及这在这里).

Jos*_*gry 10

小价值观

对于小值(基本上任何可以声明为volatile的字段),您可以执行以下操作:

private static volatile int backingField;

public static int Field
{
    get { return backingField; }
    set { backingField = value; }
} 
Run Code Online (Sandbox Code Playgroud)

大价值

对于大值,如果值大于32位机器上的32位或64位机器上的64位,则赋值将不是原子的.参见ECMA 335 12.6.6规范.因此,对于引用类型和大多数内置值类型,赋值是原子的,但是如果你有一些大的结构,比如:

struct BigStruct 
{
    public long value1, valuea0a, valuea0b, valuea0c, valuea0d, valuea0e;
    public long value2, valuea0f, valuea0g, valuea0h, valuea0i, valuea0j;
    public long value3;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您需要在get访问器周围进行某种锁定.您可以使用ReaderWriterLockSlim我在下面演示的内容.Joe Duffy 对使用vs 有建议:ReaderWriterLockSlimReaderWriterLock

    private static BigStruct notSafeField;
    private static readonly ReaderWriterLockSlim slimLock = 
        new ReaderWriterLockSlim();

    public static BigStruct Safe
    {
        get
        {
            slimLock.EnterReadLock();
            var returnValue = notSafeField;
            slimLock.ExitReadLock();

            return returnValue;
        }
        set
        {
            slimLock.EnterWriteLock();
            notSafeField = value;
            slimLock.ExitWriteLock();
        }
    }
Run Code Online (Sandbox Code Playgroud)

不安全的Get-Accessor演示

这是我用来表示在get-accessor中不使用锁时缺乏原子性的代码:

    private static readonly object mutexLock = new object();
    private static BigStruct notSafeField;

    public static BigStruct NotSafe
    {
        get
        {
            // this operation is not atomic and not safe
            return notSafeField;
        }
        set
        {
            lock (mutexLock)
            {
                notSafeField = value;
            }
        }
    }

    public static void Main(string[] args)
    {
        var t = new Thread(() =>
            {
                while (true)
                {
                    var current = NotSafe;
                    if (current.value2 != (current.value1 * 2)
                        || current.value3 != (current.value1 * 5))
                    {
                        throw new Exception(String.Format("{0},{1},{2}", current.value1, current.value2, current.value3));
                    }
                }
            });
        t.Start();
        for(int i=0; i<50; ++i)
        {
            var w = new Thread((state) =>
                {
                    while(true)
                    {
                        var index = (int) state;
                        var newvalue = new BigStruct();
                        newvalue.value1 = index;
                        newvalue.value2 = index * 2;
                        newvalue.value3 = index * 5;
                        NotSafe = newvalue;
                    }
                });
            w.Start(i);
        }
        Console.ReadLine();
    }
Run Code Online (Sandbox Code Playgroud)

  • PS:对于这些类型的锁,使用try / finally是一个好主意。您不想以抛出的异常将事物锁定为最终结果。 (2认同)

And*_*are 3

最安全、最短的方法是创建一个Object仅用于锁定的私有静态类型字段(将其视为“挂锁”对象)。使用此且仅此字段来锁定,因为这可以防止其他类型在锁定与您相同的类型时锁定您的代码。

如果您锁定类型本身,则存在另一种类型也决定锁定您的类型的风险,这可能会造成死锁。

这是一个例子:

class Test
{
    static readonly Object fooLock = new Object();
    static String foo;

    public static String Foo
    {
        get { return foo; }
        set
        {
            lock (fooLock)
            {
                foo = value;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我创建了一个用于锁定的私有静态字段foo- 我使用该字段来锁定该字段上的写入操作。

  • 本例中的锁是无用的,不会阻止任何竞争条件...正确的方法是根本不在类级别进行同步,而是提供一个 SyncRoot 对象,所有线程在对对象执行读写操作之前都应锁定该对象。财产。 (4认同)