龙空技术网

.NET 多线程编程和并发控制技术总结

码上尽宝 785

前言:

当前大家对“net高并发处理”大体比较关注,大家都需要剖析一些“net高并发处理”的相关知识。那么小编在网上收集了一些对于“net高并发处理””的相关知识,希望朋友们能喜欢,姐妹们一起来了解一下吧!

随互联网发展,多线程和并发控制成为了必不可少的技术,本文分两部分:第一部分主要介绍单进程下面多线程并发控制;第二部分主要介绍多进程分布式架构下面并发控制。欢迎大家一起学习,批评指导。

多线程编程模型下并发控制

多线程编程模型是指将应用程序分解为多个小任务,并通过多个线程并发地执行这些小任务,当然存在多个线程同时执行相同任务,要解决这个并发问题,是多线程编程的难点也是重点。在 .NET 中,可以使用如下几种常见的并发控制技术来实现并发控制。

【1. lock】

lock 是 .NET 中最基本的并发控制技术之一,它可以防止多个线程同时访问同一个资源。在 C# 中,可以使用 lock 关键字来实现锁定:

private object _lock = new object();private int _count = 0;public void Increment(){    lock (_lock)    {        _count++;    }}public void Decrement(){    lock (_lock)    {        _count--;    }}

以上代码创建了一个计数器类,该类使用 lock 来保证对计数器的操作是线程安全的。lock 关键字的工作原理是如果一个线程获得了锁,则其他线程必须等待该线程释放锁之后才能获得锁。lock 适用于只有少数几个线程并发访问的情况,当并发访问量较大时,可能会产生性能问题。

【2. Monitor】

Monitor 是 .NET 中另一个基本的并发控制技术,它可以实现与 lock 相同的功能,但提供更灵活的控制机制。在 C# 中,可以使用 Monitor 类来实现锁定:

private object _lock = new object();private int _count = 0;public void Increment(){    Monitor.Enter(_lock);    try    {        _count++;    }    finally    {        Monitor.Exit(_lock);    }}public void Decrement(){    Monitor.Enter(_lock);    try    {        _count--;    }    finally    {        Monitor.Exit(_lock);    }}

以上代码与使用 lock 关键字的计数器类相同,但使用的是 Monitor 类来实现锁定。Monitor 提供了更多配置选项,以便更灵活地控制锁定的执行过程。

【3. Semaphore】

Semaphore 是指一个计数器,它限制对共享资源的访问量。在 .NET 中,可以使用 Semaphore 类来实现 Semaphore:

private Semaphore _semaphore = new Semaphore(1, 1);public void AccessResource(){    _semaphore.WaitOne();    try    {        // 对共享资源的访问    }    finally    {        _semaphore.Release(1);    }}

以上代码创建了一个使用 Semaphore 的类,该类限制对共享资源的访问数量为 1。在访问共享资源时,线程将尝试获取信号量,如果信号量已经被占用,则线程阻塞等待直到其他线程释放信号量。信号量的最大好处是可以控制,并发的数量,对于有些任务可以运行比如多个线程同时执行。

【4. 原子性并发集合】

原子性并发集合,这个是我理解的概念,如果大家觉得有更好的名称,欢迎大家留言。他主要包括如下几种:

ConcurrentBag<T>: 用于存储对象的无序集合,可以添加、移除和获取元素。ConcurrentDictionary<TKey, TValue>: 可以在多个线程之间安全地添加、移除和更新元素的字典集合。ConcurrentQueue<T>: FIFO(先进先出)顺序的集合,用于线程安全的操作从队列的末尾添加元素,从队列的前面删除元素,以及获取队列中下一个元素。ConcurrentStack<T>: LIFO(后进先出)顺序的集合,它提供了对元素的线程安全的添加、移除和获取的操作。ConcurrentBag<T>: 无序集合,它支持线程安全和高效的添加和获取元素操作。

这些集合提供了一种简单而强大的方式来管理线程之间的共享数据,并且可以在高并发环境下保护数据塑造数据结构。它们都是线程安全的,这意味着多个线程可以通过这些集合进行访问,而不会导致竞争条件或其他线程相关问题。这类实例大家自己查找,使用方式类似集合,这类不累赘,个人感觉并发集合属于线程安全数据机构,已经帮我们控制好并发问题。在并发控制上不需要我们人为介入。这类稍微滤过。

分布式模式并发控制

在分布式架构中,由于数据存储在多个节点上,因此需要考虑更复杂的并发控制技术来保证数据的一致性和可靠性。尤其选择.net core 指出跨平台,在云原生也广泛应用,在docker 与 K8s 下面实现自动扩容伸缩,高并发模式下面控制好并发控制更为复杂和重要。但是技术其实也非常成熟,我想大家都知道.NET 中可以使用分布式锁、分布式事务、乐观锁等方式来实现分布式并发控制。下面我简单用伪代码方式阐述这三种方式的使用。

【1. 分布式锁】

分布式锁是一种用于协调分布式系统中各个节点之间并发操作的机制。在 .NET 中,可以使用第三方框架如 Redis 或 ZooKeeper 等来实现分布式锁。

下面是一个使用 Redis 实现分布式锁的示例代码:

public class DistributedLock{    private readonly IDatabase _redisDb;    private readonly string _lockKey;    private readonly string _lockValue;    public DistributedLock(IDatabase redisDb, string lockKey, string lockValue)    {        _redisDb = redisDb;        _lockKey = lockKey;        _lockValue = lockValue;    }    public bool Acquire()    {        return _redisDb.StringSet(_lockKey, _lockValue, TimeSpan.FromSeconds(10), When.NotExists);    }    public bool Release()    {        return _redisDb.KeyDelete(_lockKey);    }}

以上代码演示了如何在 .NET 中使用 Redis 实现分布式锁。Acquire() 方法用于获取分布式锁,Release() 方法用于释放分布式锁。.NET 中利用中间件实现分布式锁主要有:Redis,ZooKeeper,Etcd、Consul 甚至关系数据库也可以。如果有需要留言我可以给出详细示例。

【2. 分布式事务】

分布式事务是一种用于协调分布式系统中各个节点之间事务操作的机制。在 .NET 中,可以使用 Microsoft Distributed Transaction Coordinator(DTC)来实现分布式事务,或者使用一些第三方框架如 NServiceBus 或 MassTransit 等。

下面是一个使用 DTC 实现分布式事务的示例代码:

using (var scope = new TransactionScope(TransactionScopeOption.Required,    new TransactionOptions    {        IsolationLevel = IsolationLevel.Serializable,        Timeout = TimeSpan.FromSeconds(30)    },    EnterpriseServicesInteropOption.Full)){    using (var context = new DataContext())    {        // 执行事务操作        context.SaveChanges();    }    scope.Complete();}

DTC 实现分布式事务 个人感觉有局限性,本人只在windows 服务器上开启DTC服务有实践经验。在云原生下面没有使用过。当然选择云服商也提供分布式事务组件,如阿里云GTS等。

【3. 乐观锁】

在分布式系统中,由于存在网络延迟等原因,使用乐观锁比悲观锁更为适合。在 .NET 中,可以使用版本号或时间戳等方式来实现乐观锁。

public class Product{    public int Id { get; set; }    public string Name { get; set; }    public int Version { get; set; }    public decimal Price { get; set; }}public class ProductRepository{    public bool UpdateProduct(Product product)    {        using (var context = new DataContext())        {            var existing = context.Products.Find(product.Id);            if (existing != null && existing.Version == product.Version)            {                existing.Version++;                context.Entry(existing).CurrentValues.SetValues(product);                context.SaveChanges();                return true;            }            return false;        }    }}

以上代码使用版本号来实现乐观锁,检查 existing 对象的 Version 属性是否与传入的 product 对象的 Version 属性相同,如果相同,则递增 Version 属性并保存更改。

本人在使用三种模式下面的心得:如果ORM 乐观锁能解决的问题,优先使用乐观锁。原因乐观锁少引用了中间件(如redis)。如果有团队中有实施成熟的分布式中间件,可以考虑考虑用分布式锁。慎用DTC分布式事务.

标签: #net高并发处理