【C#】知识点实践序列之Lock简单解决并发引起数据重复问题

欢迎来到《小5讲堂之知识点实践序列》文章,大家好,我是全栈小5。
这是2024年第3篇文章,此篇文章是C#知识点实践序列文章,博主能力有限,理解水平有限,若有不对之处望指正!
本篇在Lock锁定代码块基础上进行简单解决并发问题,确保不会出现重复数据。

在这里插入图片描述

在这里插入图片描述

前言

前两篇文章已经回顾基本概念一级Lock锁定代码块的实践例子,以及简单了解了多线程的基本信息。

实践场景

有一个用户注册的功能,通过简单并发测试,在不进行索引等情况下,对比加锁和不加锁,用户表重复记录情况

用户编码重复

在并发情况下,如果方法代码和数据库没有做唯一处理,那么进行用户注册时,用户编码重复概率是很大的。
下面是逻辑是,设置3个异步方法模拟3个并发访问,他们之间就极有可能是同时执行同一个方法,用户编码就可能会重复。

效果

在这里插入图片描述

  • 重复数据
    从下图可以知道,GZ0008、GZ0018、GZ00021,这就会出现重复了,相对于用户编码就不是唯一,也就无法满足实际场景。
    在这里插入图片描述

代码

namespace XxxData
{
    /// <summary>
    /// 解决数据重复
    /// </summary>
    public partial class LockUser : Form
    {
        public LockUser()
        {
            InitializeComponent();

            CheckForIllegalCrossThreadCalls = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        int userCodeIndex = 0;
        private void TargetData()
        {
            userCodeIndex += 1;
            txtInfo.AppendText($"GZ{userCodeIndex.ToString().PadLeft(5, '0')}\r\n");
        }

        private void btnA_Click(object sender, EventArgs e)
        {
            // ===模拟并发===
            // 用户并发a区域
            Task.Run(() =>
            {
                for(int i = 0; i < 10; i++)
                {
                    TargetData();
                    Thread.Sleep(10);
                }
            });

            // 用户并发b区域
            Task.Run(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    TargetData();
                    Thread.Sleep(10);
                }
            });

            // 用户并发c区域
            Task.Run(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    TargetData();
                    Thread.Sleep(10);
                }
            });
        }
    }
}

用户编码唯一

基于用户编码唯一原则,那么解决的方法有很多,这里我们通过锁定代码块的方式来解决,也就是同步机制来解决。
同步机制的最大特点就是,即使有多个请求同一时间调用同一个方法,它也会遵循一个一个执行完,从而用户编码也只会顺序叠加。

效果

  • 方法部分锁
int userCodeIndex = 0;
private void TargetData(int num)
{
    txtInfo.AppendText($"用户【{num}】进入方法,等待添加!\r\n");

    lock (this)
    {
        userCodeIndex += 1;
        txtInfo.AppendText($"GZ{userCodeIndex.ToString().PadLeft(5, '0')}\r\n\r\n");
    }
}

在这里插入图片描述

  • 方法全部锁
int userCodeIndex = 0;
private void TargetData(int num)
{
    lock (this)
    {
        txtInfo.AppendText($"用户【{num}】进入方法,等待添加!\r\n");

        userCodeIndex += 1;
        txtInfo.AppendText($"GZ{userCodeIndex.ToString().PadLeft(5, '0')}\r\n\r\n");
    }
}

在这里插入图片描述

代码

namespace XxxData
{
    /// <summary>
    /// 解决数据重复
    /// </summary>
    public partial class LockUser : Form
    {
        public LockUser()
        {
            InitializeComponent();

            CheckForIllegalCrossThreadCalls = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        int userCodeIndex = 0;
        private void TargetData(int num)
        {
            lock (this)
            {
                txtInfo.AppendText($"用户【{num}】进入方法,等待添加!\r\n");
                userCodeIndex += 1;
                txtInfo.AppendText($"GZ{userCodeIndex.ToString().PadLeft(5, '0')}\r\n\r\n");
            }
        }

        private void btnA_Click(object sender, EventArgs e)
        {
            // ===模拟并发===
            // 用户并发a区域
            Task.Run(() =>
            {
                for(int i = 0; i < 10; i++)
                {
                    TargetData(i + 1);
                    Thread.Sleep(10);
                }
            });

            // 用户并发b区域
            Task.Run(() =>
            {
                for (int i = 10; i < 20; i++)
                {
                    TargetData(i + 1);
                    Thread.Sleep(10);
                }
            });

            // 用户并发c区域
            Task.Run(() =>
            {
                for (int i = 20; i < 30; i++)
                {
                    TargetData(i + 1);
                    Thread.Sleep(10);
                }
            });
        }
    }
}

并发基本概念

C# 并发与方法和 API 接口请求之间有一些关联和区别:
1.并发方法调用:在 C# 中,可以使用多线程或任务并发地调用方法。这种方式适用于需要同时执行多个方法并获得结果的场景。通过多线程或任务的并发,可以提高系统的响应能力和吞吐量。

2.API 接口请求:API 接口请求是通过网络协议发送请求,获取相应的资源或执行操作。在 C# 中,可以使用异步或并发技术来发起多个 API 请求,以提高性能和并发处理能力。常见的方式包括使用异步/等待模式、多线程或任务并发等。

3.并发控制:并发方法调用和 API 接口请求都需要考虑并发控制的问题。在多个线程或任务同时访问共享资源时,可能会出现竞态条件等并发问题。C# 中提供了锁、互斥体、信号量等机制来实现并发控制,以确保共享资源的安全访问。

总结:温故而知新,不同阶段重温知识点,会有不一样的认识和理解,博主将巩固一遍知识点,并以实践方式和大家分享,若能有所帮助和收获,这将是博主最大的创作动力和荣幸。也期待认识更多优秀新老博主。

  • 43
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
C#异步编程中,过度并发可能会导致性能问题,例如线程竞争、资源争用等。以下是一些解决过度并发导致性能问题的方法: 1. 限制并发数:通过限制同时执行的异步任务数量,可以避免过度并发导致的性能问题。可以使用`SemaphoreSlim`或自定义的计数机制来控制并发数。 ```csharp SemaphoreSlim semaphore = new SemaphoreSlim(maxConcurrency); async Task MyAsyncMethod() { await semaphore.WaitAsync(); try { // 异步操作代码 } finally { semaphore.Release(); } } ``` 2. 使用异步锁:如果异步操作需要访问共享资源,并且存在并发访问的风险,可以使用异步锁(`AsyncLock`)来保护共享资源的访问。 ```csharp AsyncLock asyncLock = new AsyncLock(); async Task MyAsyncMethod() { using (await asyncLock.LockAsync()) { // 异步操作代码 } } ``` 3. 优化资源使用:如果并发操作涉及到共享资源,可以通过优化资源的使用来减少竞争和争用。例如,使用更高效的数据结构、减少锁的粒度、避免不必要的资源复制等。 4. 使用合适的并发集合:在多线程环境下,如果需要共享和操作数据集合,可以使用适当的并发集合类(如`ConcurrentQueue`、`ConcurrentStack`、`ConcurrentDictionary`等)来减少锁竞争,提高性能。 5. 调整线程池参数:C#中的异步操作通常使用线程池来管理线程。根据实际情况,可以通过调整线程池的配置参数(如最大线程数、最小线程数等)来适应不同的并发负载和系统资源限制。 通过综合使用上述方法,可以有效解决C#异步编程中由过度并发导致的性能问题。需要根据具体情况评估并调整并发策略,以提高程序的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈小5

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值