首页 > 学院 > 开发设计 > 正文

C#并行编程-线程同步原语

2019-11-17 02:56:21
字体:
来源:转载
供稿:网友

C#并行编程-线程同步原语

菜鸟学习并行编程,参考《C#并行编程高级教程.PDF》,如有错误,欢迎指正。

目录

  • C#并行编程-相关概念

  • C#并行编程-Parallel

  • C#并行编程-Task

  • C#并行编程-并发集合

  • C#并行编程-线程同步原语

  • C#并行编程-PLINQ:声明式数据并行

背景

有时候必须访问变量、实例、方法、属性或者结构体,而这些并没有准备好用于并发访问,或者有时候需要执行部分代码,而这些代码必须单独运行,这是不得不通过将任务分解的方式让它们独立运行。

当任务和线程要访问共享的数据和资源的时候,您必须添加显示的同步,或者使用原子操作或锁。

之前的.NET Framework提供了昂贵的锁机制以及遗留的多线程模型,新的数据结构允许细粒度的并发和并行化,并且降低一定必要的开销,这些数据结构称为轻量级同步原语。

这些数据结构在关键场合下能够提供更好的性能,因为它们能够避免昂贵的锁机制,如果在等待时间不短的情况下使用它们,这些原语会增加额外的开销。

如果您需要特定的执行顺序,可以通过添加显示同步来实现。

同步原语

.NET Framework 4在现在的System.Threading命名空间中提供了6个同步原语,通过这个命名空间可以访问遗留的线程类、类型和枚举,还提供了新的基于任务的编程模型及特定情形紧密相关的数据结构

Barrier 使多个任务能够采用并行方式依据某种算法在多个阶段中协同工作 通过屏障

CountdownEvent 表示在计数变为0时处于有信号状态的同步基元 通过信号机制

ManualResetEventSlim允许很多任务等待直到另一个任务手工发出事件句柄,当预计等待时间很短的时候,ManualResetEventSlim 的性能比对应的重量级ManualResetEvent的性能要高。通过信号机制

SemaphoreSlim 限制对可同时访问资源或资源池的线程数,比对应的Semaphore性能要高 通过信号机制

SpinLock 提供一个相互排斥锁基元,在该基元中,尝试获得锁的线程将在重复检查的循环中等待,直至该锁变为可用为止。

SpinWait 提供对基于自旋的等待的支持。

通过屏障同步并发任务 Barrier

当在需要一组任务并行地运行一连串的阶段,但是每一个阶段都要等待其他任务完成前一阶段之后才能开始时,您可以通过使用Barrier类的实例来同步这一类协同工作,通过屏障

下面贴代码方便大家理解,如有问题,请指正,详情见注释:

    class PRogram    {        private static Task[] _CookTasks { get; set; }        private static Barrier _barrier { get; set; }        /*获取当前计算机处理器数*/        private static int _particpants = Environment.ProcessorCount;        /*  coder:释迦苦僧           *  代码中 展示煮饭的步骤   1.打水  2.淘米 3.放入锅中 4.盖上锅盖 5.生火煮饭          */        static void Main(string[] args)        {            Console.WriteLine("定义{0}个人煮饭3次", _particpants);            _CookTasks = new Task[_particpants];            _barrier = new Barrier(_particpants, (barrier) =>            {                Console.WriteLine("当前阶段:{0}", barrier.CurrentPhaseNumber);            });            Stopwatch swTask1 = new Stopwatch();            swTask1.Start();            /*定义N个人*/            for (int cook_person = 0; cook_person < _particpants; cook_person++)            {                _CookTasks[cook_person] = Task.Factory.StartNew((num) =>                {                    int index = Convert.ToInt32(num);                    /*每个人煮3次饭*/                    for (int cook_count = 0; cook_count < 3; cook_count++)                    {                        CookStepTask1(index, cook_count);                        CookStepTask2(index, cook_count);                        CookStepTask3(index, cook_count);                        CookStepTask4(index, cook_count);                        CookStepTask5(index, cook_count);                    }                }, cook_person);            }            /*ContinueWhenAll 提供一组任务完成后 延续方法*/            var finalTask = Task.Factory.ContinueWhenAll(_CookTasks, (tasks) =>            {                /*等待任务完成*/                Task.WaitAll(_CookTasks);                swTask1.Stop();                Console.WriteLine("采用并发 {1}个人煮3次饭耗时:{0}", swTask1.ElapsedMilliseconds, _particpants);                /*释放资源*/                _barrier.Dispose();            });            Thread.Sleep(4000);            Stopwatch swTask = new Stopwatch();            swTask.Start();            /*定义N个人*/            for (int cook_person = 0; cook_person < _particpants; cook_person++)            {                /*每个人煮3次饭*/                for (int cook_count = 0; cook_count < 3; cook_count++)                {                    CookStep1(cook_person, cook_count); CookStep2(cook_person, cook_count); CookStep3(cook_person, cook_count); CookStep4(cook_person, cook_count); CookStep5(cook_person, cook_count);                }            }            swTask.Stop();            Console.WriteLine("不采用并发 {1}个人煮3次饭耗时:{0}", swTask.ElapsedMilliseconds, _particpants);            Thread.Sleep(2000);            Console.ReadLine();        }        /*1.打水*/        private static void CookStepTask1(int pesron_index, int index)        {            Console.WriteLine("{0} 第{1}次 打水... 耗时2分钟", pesron_index, index);            Thread.Sleep(200);            /*存在线程暂停 所以需要将 _barrier.SignalAndWait();放在方法中 */            _barrier.SignalAndWait();        }        /*2.淘米*/        private static void CookStepTask2(int pesron_index, int index)        {            Console.WriteLine("{0} 第{1}次 淘米... 耗时3分钟", pesron_index, index);            Thread.Sleep(300);            /*存在线程暂停 所以需要将 _barrier.SignalAndWait();放在方法中 */            _barrier.SignalAndWait();        }        /*3.放入锅中*/        private static void CookStepTask3(int pesron_index, int index)        {            Console.WriteLine("{0} 第{1}次 放入锅中... 耗时1分钟", pesron_index, index);            Thread.Sleep(100);            /*存在线程暂停 所以需要将 _barrier.SignalAndWait();放在方法中 */            _barrier.SignalAndWait();        }        /*4.盖上锅盖*/        private static void CookStepTask4(int pesron_index, int index)        {            Console.WriteLine("{0} 第{1}次 盖上锅盖... 耗时1分钟", pesron_index, index);            Thread.Sleep(100);            /*存在线程暂停 所以需要将 _barrier.SignalAndWait();放在方法中 */            _barrier.SignalAndWait();        }        /*5.生火煮饭*/        private static void CookStepTask5(int pesron_index, int index)        {            Console.WriteLine("{0} 第{1}次  生火煮饭... 耗时30分钟", pesron_index, index);            Thread.Sleep(500);            /*存在线程暂停 所以需要将 _barrier.SignalAndWait();放在方法中 */            _barrier.SignalAndWait();        }        /*1.打水*/        private static void CookStep1(int pesron_index, int index)        {            Console.WriteLine("{0} 第{1}次 打水... 耗时2分钟", pesron_index, index);            Thread.Sleep(200);        }        /*2.淘米*/        private static void CookStep2(int pesron_index, int index)        {            Console.WriteLine("{0} 第{1}次 淘米... 耗时3分钟", pesron_index, index);            Thread.Sleep(300);        }        /*3.放入锅中*/        private static void CookStep3(int pesron_index, int index)        {            Console.WriteLine("{0} 第{1}次 放入锅中... 耗时1分钟", pesron_index, index);            Thread.Sleep(100);        }        /*4.盖上锅盖*/        private static void CookStep4(int pesron_index, int index)        {            Console.WriteLine("{0} 第{1}次 盖上锅盖... 耗时1分钟", pesron_index, index);            Thread.Sleep(100);        }        /*5.生火煮饭*/        private static void CookStep5(int pesron_index, int index)        {            Console.WriteLine("{0} 第{1}次  生火煮饭... 耗时30分钟", pesron_index, index);            Thread.Sleep(500);        }    }    class Product    {        public string Name { get; set; }        public string Category { get; set; }        public int SellPrice { get; set; }    }
View Code

如代码所示,在串行代码中,虽然任务是有序进行,但是等待的时间很长,因为只是在一个处理器下进行处理,如下图所示:

而采用并发处理中,使用Barrier,不仅保证了任务的有序进行,还在性能损耗上得到了最大程度的降低,如下图

ContinueWhenAll 提供一组任务完成后的延续方法

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表