浅谈.NET下的多线程和并行计算(十三)CLR via C#第三版阅读笔记(2)

简介: 线程同步的问题 1) 我们必须找到代码中所有可能被多个线程同时访问的资源,然后使用线程同步来保护资源,并且我们没有办法来验证是不是正确进行了线程同步,包括是否有遗漏和是否对不需要同步的资源进行同步。 2) 线程同步是有损性能的,如果某个操作大量执行,并且这个操作原先的执行时间非常短,那么如果我们对这段操作前后进行锁的申请和释放的话性能可能下降一个数量级。

线程同步的问题

1) 我们必须找到代码中所有可能被多个线程同时访问的资源,然后使用线程同步来保护资源,并且我们没有办法来验证是不是正确进行了线程同步,包括是否有遗漏和是否对不需要同步的资源进行同步。

2) 线程同步是有损性能的,如果某个操作大量执行,并且这个操作原先的执行时间非常短,那么如果我们对这段操作前后进行锁的申请和释放的话性能可能下降一个数量级。

3) 线程同步可能导致更频繁的线程创建和上下文的切换。

当然,只有在下面的情况下才需要线程同步,换句话说我们尽量不要使用下面的方案来导致线程同步:

1) 只有写操作, 如果只是访问只读对象,即使多个线程同时访问也不会改变资源,因此不需要同步,当然如果有写操作的话,即使是读操作也要考虑是不是需要同步。

2) 引用类型或者是可变的类型,如果访问的都是值类型(这里不说比如寄存器缓存的特殊情况)或者说类型是不可变的(比如string),那么我们对类型的获取和改写都是基于新的类型而不是多个线程的共享资源,因此不需要同步。

3) static静态资源,这也就是资源访问域的问题,如果这个资源并不会被多个线程访问到,也就是这个资源属于每个线程本身的,那么当然不需要同步。

最后,微软确保FCL所有静态方法线程安全,但由于性能问题FCL不保证所有实例方法线程安全,我们需要自己实现线程同步。对于我们自己的类库最好也遵从这个标准。

 

两种同步结构

1) 用户模式,硬件提供支持,速度非常快,但是在block的时候消耗CPU资源,在未争用的时候不损失性能

2) 内核模式,操作系统提供支持,速度比较慢,但是很灵活(比如可以设定超时时间,可以等待一组同步结构都可用的时候继续)并且和用户模式想法的是在block的时候可以不消耗CPU

作者进行了一个性能测试:

class Program
{
    static void Main(string[] args)
    {
        Int32 x = 0;
        const Int32 iterations = 5000000;
        Stopwatch sw = Stopwatch.StartNew();

        SimpleSpinLock ssl = new SimpleSpinLock();
        for (Int32 i = 0; i < iterations; i++)
        {
            ssl.Enter(); x++; ssl.Leave();
        }
        Console.WriteLine("Incrementing x in SimpleSpinLock: {0:N0}", sw.ElapsedMilliseconds);
        using (SimpleWaitLock swl = new SimpleWaitLock())
        {
            sw = Stopwatch.StartNew();
            for (Int32 i = 0; i < iterations; i++)
            {
                swl.Enter(); x++; swl.Leave();
            }
            Console.WriteLine("Incrementing x in SimpleWaitLock: {0:N0}", sw.ElapsedMilliseconds);
        }
    }
}

struct SimpleSpinLock
{
    private Int32 m_ResourceInUse;
    public void Enter()
    {
        while (Interlocked.Exchange(ref m_ResourceInUse, 1) != 0)
        {
        }
    }
    public void Leave()
    {
        Thread.VolatileWrite(ref m_ResourceInUse, 0);
    }
}

class SimpleWaitLock : IDisposable
{
    private AutoResetEvent m_ResourceFree = new AutoResetEvent(true);
    public void Enter()
    {
        m_ResourceFree.WaitOne();
    }
    public void Leave()
    {
        m_ResourceFree.Set();
    }
    public void Dispose() 
    {
        m_ResourceFree.Close();
    }
}

结果表明基于内核模式的同步比基于用户模式的同步慢了数十倍:

image

有关内核模式的各种结构我们在之前的文章中介绍了很多,在这里再补充一下两种用户模式的同步结构:

1) Volatile

两个作用一是防止编译器JIT甚至CPU对代码和指令等进行优化(比如跳过判断,调整代码次序),二是防止多核CPU分别在自己的寄存器中缓存了值导致的不一致性,让值始终从内存中读取。

2) Interlocked

Interlocked.Exchange和Interlocked.CompareExchange分别用于确保赋值操作和比较赋值操作的原子性,值得一提的是,Interlocked.Exchange并没有提供bool重载,我们可以使用int的0和1替代。还有,Interlocked.Read只提供了long重载,那是因为读一个long值的任何操作在32位操作系统上不是原子行为,而在64位操作系统上long的读操作是原子的。

作者: lovecindywang
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
20天前
|
JSON C# 开发者
C#语言新特性深度剖析:提升你的.NET开发效率
【10月更文挑战第15天】C#语言凭借其强大的功能和易用性深受开发者喜爱。随着.NET平台的演进,C#不断引入新特性,如C# 7.0的模式匹配和C# 8.0的异步流,显著提升了开发效率和代码可维护性。本文将深入探讨这些新特性,助力开发者在.NET开发中更高效地利用它们。
29 1
|
1月前
|
存储 消息中间件 NoSQL
Redis 入门 - C#.NET Core客户端库六种选择
Redis 入门 - C#.NET Core客户端库六种选择
53 8
|
26天前
|
人工智能 开发框架 C#
C#/.NET/.NET Core技术前沿周刊 | 第 6 期(2024年9.16-9.22)
C#/.NET/.NET Core技术前沿周刊 | 第 6 期(2024年9.16-9.22)
|
26天前
|
人工智能 开发框架 Cloud Native
C#/.NET/.NET Core技术前沿周刊 | 第 9 期(2024年10.07-10.13)
C#/.NET/.NET Core技术前沿周刊 | 第 9 期(2024年10.07-10.13)
|
26天前
|
开发框架 前端开发 API
C#/.NET/.NET Core优秀项目和框架2024年9月简报
C#/.NET/.NET Core优秀项目和框架2024年9月简报
|
26天前
|
开发框架 NoSQL MongoDB
C#/.NET/.NET Core开发实战教程集合
C#/.NET/.NET Core开发实战教程集合
|
26天前
|
数据可视化 NoSQL C#
C#/.NET/.NET Core技术前沿周刊 | 第 8 期(2024年10.01-10.06)
C#/.NET/.NET Core技术前沿周刊 | 第 8 期(2024年10.01-10.06)
|
26天前
|
开发框架 缓存 算法
开源且实用的C#/.NET编程技巧练习宝库(学习,工作,实践干货)
开源且实用的C#/.NET编程技巧练习宝库(学习,工作,实践干货)
|
26天前
|
边缘计算 开发框架 人工智能
C#/.NET/.NET Core优秀项目和框架2024年8月简报
C#/.NET/.NET Core优秀项目和框架2024年8月简报
|
26天前
|
设计模式 开发框架 C#
C#/.NET/.NET Core技术前沿周刊 | 第 4 期(2024年9.1-9.8)
C#/.NET/.NET Core技术前沿周刊 | 第 4 期(2024年9.1-9.8)