Thread.MemoryBarrier和一个简单的属性锁差 [英] Thread.MemoryBarrier and lock difference for a simple property

查看:148
本文介绍了Thread.MemoryBarrier和一个简单的属性锁差的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于下列情形,有什么区别关于线程safeness,结果和使用之间的性能 MemoryBarrier

 私人SOMETYPE场;

公共SOMETYPE物业
{
    得到
    {
        Thread.MemoryBarrier();
        SOMETYPE结果=场;
        Thread.MemoryBarrier();
        返回结果;
    }
    组
    {
        Thread.MemoryBarrier();
        字段=值;
        Thread.MemoryBarrier();
    }
}
 

锁定语句(<$ C C $> Monitor.Enter 和 Monitor.Exit

 私人SOMETYPE场;
私人只读对象SYNCLOCK =新的对象();

公共SOMETYPE物业
{
    得到
    {
        锁定(SYNCLOCK)
        {
            回场;
        }
    }
    组
    {
        锁定(SYNCLOCK)
        {
            字段=值;
        }
    }
}
 

由于引用赋值是原子的,所以我认为,在这个情况下,我们确实需要任何锁定机制。

性能 该MemeoryBarrier约2倍比锁实现发行速度更快。下面是我的测试结果:

 锁定
Normaly:5397毫秒
传递接口:5431毫秒

双重屏障
Normaly:2786毫秒
传递接口:3754毫秒

挥发物
Normaly:250毫秒
传递接口:668毫秒

易失读/写
Normaly:253毫秒
传递接口:697毫秒

ReaderWriterLockSlim
Normaly:9272毫秒
传递接口:10040毫秒

单个障碍:房产的新鲜度
Normaly:1491毫秒
传递接口:2510毫秒

单屏障:其他不reodering
Normaly:1477毫秒
传递接口:2275毫秒
 

下面是我在LINQPad测试它(在preferences优化集):

 无效的主要()
{
    锁使用.dump();
    串温度;
    VAR一个=新的A();
    VAR手表= Stopwatch.StartNew();
    的for(int i = 0; I&LT;亿; ++ I)
    {
        TEMP = a.Property;
        a.Property =温度;
    }
    Console.WriteLine(Normaly:+ watch.ElapsedMilliseconds +毫秒);
    试验(a);

    双障碍使用.dump();
    变种B =新的B();
    watch.Restart();
    的for(int i = 0; I&LT;亿; ++ I)
    {
        TEMP = b.Property;
        b.Property =温度;
    }
    Console.WriteLine(Normaly:+ watch.ElapsedMilliseconds +毫秒);
    测试(二);

    挥发性使用.dump();
    变种C =新C();
    watch.Restart();
    的for(int i = 0; I&LT;亿; ++ I)
    {
        TEMP = c.Property;
        c.Property =温度;
    }
    Console.WriteLine(Normaly:+ watch.ElapsedMilliseconds +毫秒);
    测试(C);

    易失读/写使用.dump();
    变种D =新的D();
    watch.Restart();
    的for(int i = 0; I&LT;亿; ++ I)
    {
        TEMP = d.Property;
        d.Property =温度;
    }
    Console.WriteLine(Normaly:+ watch.ElapsedMilliseconds +毫秒);
    试验(四);

    ReaderWriterLockSlim使用.dump();
    变种E =新E();
    watch.Restart();
    的for(int i = 0; I&LT;亿; ++ I)
    {
        TEMP = e.Property;
        e.Property =温度;
    }
    Console.WriteLine(Normaly:+ watch.ElapsedMilliseconds +毫秒);
    测试(E);

    单个障碍:房产的新鲜使用.dump();
    变种F =新的F();
    watch.Restart();
    的for(int i = 0; I&LT;亿; ++ I)
    {
        TEMP = f.Property;
        f.Property =温度;
    }
    Console.WriteLine(Normaly:+ watch.ElapsedMilliseconds +毫秒);
    测试(F);

    单屏障:其他不reodering使用.dump();
    变种G =新G();
    watch.Restart();
    的for(int i = 0; I&LT;亿; ++ I)
    {
        TEMP = g.Property;
        g.Property =温度;
    }
    Console.WriteLine(Normaly:+ watch.ElapsedMilliseconds +毫秒);
    测试(G);
}

无效测试(我一个)
{
    串温度;
    VAR手表= Stopwatch.StartNew();
    的for(int i = 0; I&LT;亿; ++ I)
    {
        TEMP = a.Property;
        a.Property =温度;
    }

    Console.WriteLine(传递接口:+ watch.ElapsedMilliseconds +MS \ N);
}

接口I
{
    string属性{获得;组; }
}

A级:我
{
    私人字符串场;
    私人只读对象SYNCLOCK =新的对象();

    公共字符串属性
    {
        得到
        {
            锁定(SYNCLOCK)
            {
                回场;
            }
        }
        组
        {
            锁定(SYNCLOCK)
            {
                字段=值;
            }
        }
    }
}

B类:我
{
    私人字符串场;

    公共字符串属性
    {
        得到
        {
            Thread.MemoryBarrier();
            字符串结果=场;
            Thread.MemoryBarrier();
            返回结果;
        }
        组
        {
            Thread.MemoryBarrier();
            字段=值;
            Thread.MemoryBarrier();
        }
    }
}

C类:我
{
    私人挥发性串场;

    公共字符串属性
    {
        得到
        {
            回场;
        }
        组
        {
            字段=值;
        }
    }
}

D类:我
{
    私人字符串场;

    公共字符串属性
    {
        得到
        {
            返回Volatile.Read(ref字段);
        }
        组
        {
            Volatile.Write(REF域,值);
        }
    }
}

E类:我
{
    私人字符串场;
    私人ReaderWriterLockSlim更衣室=新ReaderWriterLockSlim();

    公共字符串属性
    {
        得到
        {
            locker.EnterReadLock();
            字符串结果=场;
            locker.ExitReadLock();
            返回结果;
        }
        组
        {
            locker.EnterReadLock();
            字段=值;
            locker.ExitReadLock();
        }
    }
}

F级:我
{
    私人字符串场;

    公共字符串属性
    {
        得到
        {
            Thread.MemoryBarrier();
            回场;
        }
        组
        {
            字段=值;
            Thread.MemoryBarrier();
        }
    }
}

G类:我
{
    私人字符串场;

    公共字符串属性
    {
        得到
        {
            字符串结果=场;
            Thread.MemoryBarrier();
            返回结果;
        }
        组
        {
            Thread.MemoryBarrier();
            字段=值;
        }
    }
}
 

解决方案
  

有没有关于线程safeness有什么区别?

这两个确保适当的障碍在各地设立的读写。

  

结果如何呢?

在这两种情况下,两个线程可以比赛写一个值。然而,读取和写入不能向前或向后移动的时间过去无论是锁或完整的围栏。

  

性能?

您已经c要写两种方式的$ C $。 现在运行它。如果你想知道这是更快,运行它,并找到了!如果你有两匹马,你想知道这是更快,比赛他们。难道他们不觉得这马是更快的在互联网上向陌生人。

这是说,一个更好的方法是设定绩效目标,写了code是显然是正确的,然后进行测试,看看是否满足你的目标。如果你没有,不要浪费你宝贵的时间试图进一步优化code,它已经足够快;花钱优化是不够快别的东西。

有一个问题,你没有问:

  

你会怎么做?

我不写多线程程序,这就是我想要做的。我使用的过程作为我单位的并发性,如果我不得不。

如果我不得不写一个多线程的程序,那么我会使用最高级别的工具。我使用任务并行库,我会使用异步计谋,我会使用延迟&LT; T&GT; 等。我会避免共享内存;我把线程作为异步返回的值轻量级进程。

如果我不得不写一个共享内存的多线程程序,那么我会锁定了一切,所有的时间。我们经常编写程序,这些天来获取一个十亿字节的视频通过卫星链接,并将其发送到手机上。花采取锁二十纳秒不会杀了你。

我不够聪明试着写低锁code,所以我不会那样做的。如果我要和我会用低锁定code建立一个更高层次的抽象并使用抽象。幸运的是我没有来,因为有人已经建有我需要的抽象概念。

For the following scenario, is there any difference regarding thread-safeness, result and performance between using MemoryBarrier

private SomeType field;

public SomeType Property
{
    get
    {
        Thread.MemoryBarrier();
        SomeType result = field;
        Thread.MemoryBarrier();
        return result;
    }
    set
    {
        Thread.MemoryBarrier();
        field = value;
        Thread.MemoryBarrier();
    }
}

and lock statement (Monitor.Enter and Monitor.Exit)

private SomeType field;
private readonly object syncLock = new object();

public SomeType Property
{
    get
    {
        lock (syncLock)
        {
            return field;
        }
    }
    set
    {
        lock (syncLock)
        {
            field = value;
        }
    }
}

Because reference assignment is atomic so I think that in this scenarios we do need any locking mechanism.

Performance The MemeoryBarrier is about 2x faster than lock implementation for Release. Here are my test results:

Lock
Normaly: 5397 ms
Passed as interface: 5431 ms

Double Barrier
Normaly: 2786 ms
Passed as interface: 3754 ms

volatile
Normaly: 250 ms
Passed as interface: 668 ms

Volatile Read/Write
Normaly: 253 ms
Passed as interface: 697 ms

ReaderWriterLockSlim
Normaly: 9272 ms
Passed as interface: 10040 ms

Single Barrier: freshness of Property
Normaly: 1491 ms
Passed as interface: 2510 ms

Single Barrier: other not reodering
Normaly: 1477 ms
Passed as interface: 2275 ms

Here is how I tested it in LINQPad (with optimization set in Preferences):

void Main()
{   
    "Lock".Dump();
    string temp;
    var a = new A();
    var watch = Stopwatch.StartNew();
    for (int i = 0; i < 100000000; ++i)
    {
        temp = a.Property;
        a.Property = temp;
    }
    Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms");
    Test(a);

    "Double Barrier".Dump();
    var b = new B();
    watch.Restart();
    for (int i = 0; i < 100000000; ++i)
    {
        temp = b.Property;
        b.Property = temp;
    }
    Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms");
    Test(b);

    "volatile".Dump();
    var c = new C();
    watch.Restart();
    for (int i = 0; i < 100000000; ++i)
    {
        temp = c.Property;
        c.Property = temp;
    }
    Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms");
    Test(c);

    "Volatile Read/Write".Dump();
    var d = new D();
    watch.Restart();
    for (int i = 0; i < 100000000; ++i)
    {
        temp = d.Property;
        d.Property = temp;
    }
    Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms");
    Test(d);

    "ReaderWriterLockSlim".Dump();
    var e = new E();
    watch.Restart();
    for (int i = 0; i < 100000000; ++i)
    {
        temp = e.Property;
        e.Property = temp;
    }
    Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms");
    Test(e);

    "Single Barrier: freshness of Property".Dump();
    var f = new F();
    watch.Restart();
    for (int i = 0; i < 100000000; ++i)
    {
        temp = f.Property;
        f.Property = temp;
    }
    Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms");
    Test(f);

    "Single Barrier: other not reodering".Dump();
    var g = new G();
    watch.Restart();
    for (int i = 0; i < 100000000; ++i)
    {
        temp = g.Property;
        g.Property = temp;
    }
    Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms");
    Test(g);
}

void Test(I a)
{
    string temp;
    var watch = Stopwatch.StartNew();
    for (int i = 0; i < 100000000; ++i)
    {
        temp = a.Property;
        a.Property = temp;
    }

    Console.WriteLine("Passed as interface: " + watch.ElapsedMilliseconds + " ms\n");
}

interface I
{
    string Property { get; set; }
}

class A : I
{
    private string field;
    private readonly object syncLock = new object();

    public string Property
    {
        get
        {
            lock (syncLock)
            {
                return field;
            }
        }
        set
        {
            lock (syncLock)
            {
                field = value;
            }
        }
    }
}

class B : I
{
    private string field;

    public string Property
    {
        get
        {
            Thread.MemoryBarrier();
            string result = field;
            Thread.MemoryBarrier();
            return result;
        }
        set
        {
            Thread.MemoryBarrier();
            field = value;
            Thread.MemoryBarrier();
        }
    }
}

class C : I
{
    private volatile string field;

    public string Property
    {
        get
        {
            return field;
        }
        set
        {
            field = value;
        }
    }
}

class D : I
{
    private string field;

    public string Property
    {
        get
        {
            return Volatile.Read(ref field);
        }
        set
        {
            Volatile.Write(ref field, value);
        }
    }
}

class E : I
{
    private string field;
    private ReaderWriterLockSlim locker = new ReaderWriterLockSlim();

    public string Property
    {
        get
        {
            locker.EnterReadLock();
            string result = field;
            locker.ExitReadLock();
            return result;
        }
        set
        {
            locker.EnterReadLock();
            field = value;
            locker.ExitReadLock();
        }
    }
}

class F : I
{
    private string field;

    public string Property
    {
        get
        {
            Thread.MemoryBarrier();
            return field;
        }
        set
        {
            field = value;
            Thread.MemoryBarrier();
        }
    }
}

class G : I
{
    private string field;

    public string Property
    {
        get
        {
            string result = field;
            Thread.MemoryBarrier();
            return result;
        }
        set
        {
            Thread.MemoryBarrier();
            field = value;
        }
    }
}

解决方案

is there any difference regarding thread-safeness?

Both ensure that appropriate barriers are set up around the read and write.

result?

In both cases two threads can race to write a value. However, reads and writes cannot move forwards or backwards in time past either the lock or the full fences.

performance?

You've written the code both ways. Now run it. If you want to know which is faster, run it and find out! If you have two horses and you want to know which is faster, race them. Don't ask strangers on the Internet which horse they think is faster.

That said, a better technique is set a performance goal, write the code to be clearly correct, and then test to see if you met your goal. If you did, don't waste your valuable time trying to optimize further code that is already fast enough; spend it optimizing something else that isn't fast enough.

A question you didn't ask:

What would you do?

I'd not write a multithreaded program, that's what I'd do. I'd use processes as my unit of concurrency if I had to.

If I had to write a multithreaded program then I would use the highest-level tool available. I'd use the Task Parallel Library, I'd use async-await, I'd use Lazy<T> and so on. I'd avoid shared memory; I'd treat threads as lightweight processes that returned a value asynchronously.

If I had to write a shared-memory multithreaded program then I would lock everything, all the time. We routinely write programs these days that fetch a billion bytes of video over a satellite link and send it to a phone. Twenty nanoseconds spent taking a lock isn't going to kill you.

I am not smart enough to try to write low-lock code, so I wouldn't do that at all. If I had to then I would use that low-lock code to build a higher-level abstraction and use that abstraction. Fortunately I don't have to because someone already has built the abstractions I need.

这篇关于Thread.MemoryBarrier和一个简单的属性锁差的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆