多线程中,部分任务需要进行同步操作,对线程进行加锁操作,确保某个任务A抢到资源进行时,其他任务暂停等待,直到任务完成,其他任务才会顺序执行,类似于线程队列。此时可以使用lock,Monitor,Mutex,Interlocked进行加锁阻塞操作。

比如 抢红包,看似多人同时在抢,但对服务端处理逻辑函数来说,是一个一个同步处理的【比如每毫秒处理多少次】。

本篇只以InterLocked为例,多线程直接的同步锁处理示例。

一、新建控制台应用程序MultiThreadLockDemo,使用.net framework4.5.

相应的测试源程序如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MultiThreadLockDemo
{
    class Program
    {
        /// <summary>
        /// 线程同步加锁的值
        /// </summary>
        static long lockedValue = 0;
        /// <summary>
        /// 缓冲区当前字符,只能容纳一个字符
        /// </summary>
        private static char buffer;
        static void Main(string[] args)
        {
            int currentIndex = 0;//当前索引
            int totalAmount = 10000;//红包总金额【100RMB】 单位为:分【放大100倍为整数,方便处理】
            int count = 10;//抢红包人数
            Console.WriteLine($"...红包总金额【{totalAmount}】,抢红包人数【{count}】,开始模拟抢红包...");
            List<Task> taskCollection = new List<Task>();
            for (int i = 0; i < count; i++)
            {
                taskCollection.Add(Task.Factory.StartNew(() =>
                {
                    GrabGift(totalAmount, count, ref currentIndex);
                }));
            }
            //等待所有任务完成
            Task.WaitAll(taskCollection.ToArray());
            Console.WriteLine("......下面测试并行读写某段文本......");
            string message = @"偏偏秉烛夜游
午夜星辰似奔走之友
爱你每个结痂伤口
酿成的陈年烈酒
入喉尚算可口
怎么泪水还偶尔失守
邀你细看心中缺口
裂缝中留存温柔
此时已莺飞草长爱的人正在路上
我知他风雨兼程途经日暮不赏
穿越人海只为与你相拥
此刻已皓月当空爱的人手捧星光
我知他乘风破浪去了黑暗一趟
感同身受给你救赎热望
知道你不能还要你感受
让星光加了一点彩虹
让樱花偷偷吻你额头
让世间美好与你环环相扣
此时已莺飞草长爱的人正在路上
我知他风雨兼程途经日暮不赏
穿越人海只为与你相拥
此刻已皓月当空爱的人手捧星光
我知他乘风破浪去了黑暗一趟
感同身受给你救赎热望
此时已莺飞草长爱的人正在路上
我知他风雨兼程途经日暮不赏
穿越人海只为与你相拥
此刻已皓月当空爱的人手捧星光
我知他乘风破浪去了黑暗一趟
感同身受给你救赎热望
知道你不能还要你感受
让星光加了一点彩虹
当樱花开的纷纷扬扬
当世间美好与你环环相扣";
            TestParallelReadWrite(message);

Console.ReadLine();
        }

/// <summary>
        /// 测试同时(并行)读写某段文本
        /// </summary>
        /// <param name="message"></param>
        static void TestParallelReadWrite(string message)
        {
            //读取数据到缓存中,每次只读一个
            Thread threadRead = new Thread(() => 
            {
                for (int i = 0; i < message.Length; i++)
                {
                    //如果内存中有值,就死循环等待
                    while (Interlocked.Read(ref lockedValue) != 0)
                    {
                        Thread.Sleep(50);
                    }
                    buffer = message[i];
                    //将标识更新为1,内存中有值
                    Interlocked.Increment(ref lockedValue);
                }
            });
            //将缓存中的数据写入,每次只写一个
            Thread threadWrite = new Thread(() =>
            {
                for (int i = 0; i < message.Length; i++)
                {
                    //如果内存中没有值,就死循环等待
                    while (Interlocked.Read(ref lockedValue) == 0)
                    {
                        Thread.Sleep(50);
                    }
                    Console.Write(buffer);
                    //将标识还原为0
                    Interlocked.Decrement(ref lockedValue);
                }
            });
            threadRead.Start();
            threadWrite.Start();
        }

/// <summary>
        /// 用于排他锁,确保在多线程调用接口时,不会同时调用
        /// </summary>
        static int lockedFlag = 0;
        /// <summary>
        /// 每个人获取红包的最小值,每个人的收到的红包不能小于这个值
        /// </summary>
        const int giftMin = 100;//随机礼物的最小值,要分割的红包的最小值
        /// <summary>
        /// 将已抢红包放入列表
        /// </summary>
        static List<int> list = new List<int>();
        /// <summary>
        /// 抢红包
        /// </summary>
        /// <param name="totalAmount">红包金额总数</param>
        /// <param name="count">要抢红包的总人数</param>
        /// <param name="currentIndex">当前抢红包的人员索引,从0开始</param>
        static void GrabGift(int totalAmount, int count, ref int currentIndex)
        {
            //添加锁:如果初始值不为零,就一直等待
            while (Interlocked.Exchange(ref lockedFlag, 1) != 0)
            {
                Thread.Sleep(20);
            }
            
            //前面(N-1)个随机处理,为了保证每个人至少抢一元钱【giftMin】。因此随机数的最大值为{总数-已经随机后的总和-(count - i) * giftMin}
            int sum = 0;
            int currentAmount = 0;
            Random random = new Random(Guid.NewGuid().GetHashCode());
            if (currentIndex < count - 1)
            {
                Thread.Sleep(random.Next(100, 300 + currentIndex));
                sum = list.Sum();
                int maxValue = totalAmount - sum - (count - currentIndex) * giftMin;
                if (giftMin > maxValue)
                {
                    //释放锁
                    Interlocked.Exchange(ref lockedFlag, 0);
                    throw new Exception($"抢红包算法逻辑不可以,请合理规划:每人获得红包的最小值为【{giftMin}】,当前可抢红包金额【{maxValue}】");
                }
                currentAmount = random.Next(giftMin, maxValue);
                Console.WriteLine($"当前第【{currentIndex + 1}】人,抢到红包【{currentAmount}】");
                list.Add(currentAmount);
                currentIndex++;
            }
            else
            {
                //最后一个:直接是剩余的红包金额 
                sum = list.Sum();
                currentAmount = totalAmount - sum;
                Console.WriteLine($"当前第【{currentIndex + 1}】人,抢到红包【{currentAmount}】");
                list.Add(currentAmount);
            }
            //释放锁,将初始值置0
            Interlocked.Exchange(ref lockedFlag, 0);
        }
    }
}

二、程序运行如图:

关于多线程Interlocked的使用相关推荐

  1. 秒杀多线程第三篇 原子操作 Interlocked系列函数

    上一篇<多线程第一次亲密接触 CreateThread与_beginthreadex本质区别>中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是 ...

  2. 多线程笔记--原子操作Interlocked系列函数

    前面写了一个多线程报数的功能,为了描述方便和代码简洁起见,只输出最后的报数结果来观察程序运行结果.这非常类似一个网站的客户访问统计,每个用户登录用一个线程模拟,线程运行时将一个表示计数的变量递增.程序 ...

  3. C#多线程编程(6)--线程安全2 互锁构造Interlocked

    在线程安全1中,我介绍了线程同步的意义和一种实现线程同步的方法:volatile.volatile关键字属于原子操作的一种,若对一个关键字使用volatile,很多时候会显得很"浪费&quo ...

  4. C#多线程学习(四) 多线程的自动管理(线程池) (转载系列)——继续搜索引擎研究...

    在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应                   这一般使用ThreadPo ...

  5. .NET多线程编程入门

    在.NET多线程编程这个系列我们讲一起来探讨多线程编程的各个方面.首先我将在本篇文章的开始向大家介绍多线程的有关概念以及多线程编程的基础知识;在接下来的文章中,我将逐一讲述.NET平台上多线程编程的知 ...

  6. C# 多线程学习总结

    C#多线程学习(一) 多线程的相关概念 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程? 线程是 ...

  7. [转]C#多线程学习(四) 多线程的自动管理(线程池)

    在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应                   这一般使用ThreadPo ...

  8. C#多线程编程实战(二):线程同步

    2.1 简介 竞争条件:多个线程同时使用共享对象.需要同步这些线程使得共享对象的操作能够以正确的顺序执行 线程同步问题:多线程的执行并没有正确的同步,当一个线程执行递增和递减操作时,其他线程需要依次等 ...

  9. .NET 框架与多线程 (转载)

    专栏作品 .NET 框架与多线程 袁剑 .NET 框架与多线程 本文发表在<程序春秋>2003年12期 摘要 本文介绍了.NET框架的基本概念并简要的描述了如何利用.NET框架编写多线程程 ...

最新文章

  1. 【javascript】深入理解对象
  2. 室外电磁赛道铺设补充说明
  3. 【7.6.3】基于每个具体类一张表的继承映射
  4. 数据挖掘原理与算法:机器学习->{[sklearn. model_selection. train_test_split]、[h2o]、[网格搜索]、[numpy]、[plotly.express]}
  5. ionic1 打包过程 常用命令行
  6. 组态王6.55连接MySql数据库(笔记)
  7. java与jquery的选择器区别_java day44【JQuery 基础:概念,快速入门,JQuery对象和JS对象区别与转换,选择器,DOM操作,案例】...
  8. C#8.0的两个有趣的新特性以及gRPC
  9. git did not exit cleanly(解决办法)
  10. linux 文件inode,linux文件系统-inode学习整理
  11. 使用XAMPP轻松建站(下)
  12. [RocketMQ]消息中间件—RocketMQ消息消费(一)
  13. 针对业务系统的开发,如何做需求分析和设计1
  14. 数据预处理—3.变量选择之逐步挑选—向前挑选、向后挑选、双向挑选python实现
  15. android 动态获取权限
  16. 区块链入门-完整版V1.0-Part5
  17. run npm fund for details
  18. sox处理mp3_SOX 音频处理
  19. 生成SGML格式,用于算BLEU,NIST,TER
  20. 软件架构设计-大型网站技术架构于业务架构融合之道——部分知识点总结【未完】

热门文章

  1. (转)Android刷机的一些知识整理
  2. 一文读懂2021中国边缘计算上市公司业绩
  3. MATLAB GUIDE入门1
  4. Python爬虫入门系列——Urllib详解
  5. java毕业生设计蛋糕店会员系统的设计与实现计算机源码+系统+mysql+调试部署+lw
  6. JavaWeb实现增删改查
  7. 关于音视频直播那些事@布卡互动
  8. 由于管理员设置的策略,该磁盘处于脱机状态“解决办法
  9. [转贴]世界上最经典的爱情短语
  10. 手机找回方法mi10