大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C#技巧 > C#并行编程-线程同步原语

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

关键词:并行编程线程同步C#  阅读(3077) 赞(19)

[摘要]本文是对C#并行编程-线程同步原语的讲解,对学习C#编程技术有所帮助,与大家分享。

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

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

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

   /*ContinueWhenAll 提供一组任务完成后 延续方法*/
            var finalTask = Task.Factory.ContinueWhenAll(_CookTasks, (tasks) =>
            {
                /*等待任务完成*/
                Task.WaitAll(_CookTasks);
                swTask1.Stop();
                Console.WriteLine("采用并发 {1}个人煮3次饭耗时:{0}", swTask1.ElapsedMilliseconds, _particpants);
                /*释放资源*/
                _barrier.Dispose();
            });

通过屏障同步并发任务 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);
                        /*处理等待中的异常 如果等待时间超过300毫秒的话则抛出
                         * 参考方法体1中 模拟了超时操作, 则屏障等待时 如果发现超时 则处理异常
                         */
                        try
                        {
                            /*屏障 等待超过2秒钟 其执行算法有问题 超时  则抛出异常 记录信息 提醒开发人员观察*/
                            if (!_barrier.SignalAndWait(2000))
                            {
                                /*抛出超时异常*/
                                throw new OperationCanceledException("等待超时,抛出异常");
                            }
                        }
                        catch (Exception ex)
                        {
                            /*处理异常*/
                            Console.WriteLine(ex.Message);
                            continue;
                        }
                        CookStepTask2(index, cook_count);
                        _barrier.SignalAndWait();
                        CookStepTask3(index, cook_count);
                        _barrier.SignalAndWait();
                        CookStepTask4(index, cook_count);
                        _barrier.SignalAndWait();
                        CookStepTask5(index, cook_count);
                        _barrier.SignalAndWait();
                    }
                }, cook_person);
            }

            /*ContinueWhenAll 提供一组任务完成后 延续方法*/
            var finalTask = Task.Factory.ContinueWhenAll(_CookTasks, (tasks) =>
            {
                foreach (Task task in _CookTasks)
                {
                    if (task.Exception != null)
                    {
                        /*任务执行完成后  输出所有异常 打印异常报表*/
                        foreach (Exception exception in task.Exception.InnerExceptions)
                        {
                            Console.WriteLine("异常信息:{0}", exception.Message);
                        }
                    }
                }
                /*等待任务完成*/
                Task.WaitAll(_CookTasks);
                swTask1.Stop();
                Console.WriteLine("采用并发 {1}个人煮3次饭耗时:{0}", swTask1.ElapsedMilliseconds, _particpants);
                /*释放资源*/
                _barrier.Dispose();

            });

            Console.ReadLine();
        }
        /*1.打水*/
        private static void CookStepTask1(int pesron_index, int index)
        {
            Console.WriteLine("{0} 第{1}次 打水... 耗时2分钟", pesron_index, index);

            /*模拟一个方法体内异常抛出*/
            //throw new Exception("抛出一个代码异常");
            if (pesron_index == 0)
            {
                /*模拟超时操作*/
                //SpinWait.SpinUntil(() => (_barrier.ParticipantsRemaining == 0), 5000);
                Thread.Sleep(5000);
            }
        } 
        /*2.淘米*/
        private static void CookStepTask2(int pesron_index, int index)
        {
            Console.WriteLine("{0} 第{1}次 淘米... 耗时3分钟", pesron_index, index);
        }

        /*3.放入锅中*/
        private static void CookStepTask3(int pesron_index, int index)
        {
            Console.WriteLine("{0} 第{1}次 放入锅中... 耗时1分钟", pesron_index, index);
        }

        /*4.盖上锅盖*/
        private static void CookStepTask4(int pesron_index, int index)
        {
            Console.WriteLine("{0} 第{1}次 盖上锅盖... 耗时1分钟", pesron_index, index);
        }
        /*5.生火煮饭*/
        private static void CookStepTask5(int pesron_index, int index)
        {
            Console.WriteLine("{0} 第{1}次  生火煮饭... 耗时30分钟", pesron_index, index);
        }
    }

如代码所示,在 CookStepTask1 方法体中,我模拟了超时和异常,并在Task任务中,利用Barrier的SignalAndWait方法处理屏障中的超时信息,和Task中异常记录信息。

锁的特性

互斥和可见性。互斥指的是一次只允许一个线程持有某个特定的锁,因此可以保证共享数据内容的一致性;

可见性指的是必须确保锁被释放之前对共享数据的修改,随后获得锁的另一个线程能够知道该行为。

参考http://www.cnblogs.com/lucifer1982/archive/2008/03/23/1116981.html

互斥锁-System.Threading.Monitor

如果有一个临界区,一次只有一个任务能够访问这个临界区,但是这个临界区需要被很多任务循环访问,那么使用任务延续并不是一个好的选择,那么另一种替换方案就是采用互斥锁原语。

下面已操作字符串为示意,看下不采用锁,采用传统的LOCK和采用互斥锁的区别

不采用任何锁机制代码如下:

    class Program
    {
        private static Task[] _CookTasks { get; set; }
        private static object o = new object();
        private static StringBuilder AppendStrUnLock = new StringBuilder();
        private static StringBuilder AppendStrLock = new StringBuilder();
        private static StringBuilder AppendStrMonitorLock = new StringBuilder();
        /*获取当前计算机处理器数*/
        private static int _particpants = Environment.ProcessorCount;
        /*  coder:释迦苦僧   */
        static void Main(string[] args)
        { 
            _CookTasks = new Task[_particpants];
            Stopwatch swTask1 = new Stopwatch();
            swTask1.Start();
            for (int task_index = 0; task_index < _particpants; task_index++)
            {
                _CookTasks[task_index] = Task.Factory.StartNew((num) =>
                {
                    Parallel.For(1, 1000, (i) =>
                    {
                        string str = "append message " + i;
                        AppendStrUnLock.Append(str);
                    });
                }, task_index);
            }

            /*ContinueWhenAll 提供一组任务完成后 延续方法*/
            var finalTask = Task.Factory.ContinueWhenAll(_CookTasks, (tasks) =>
            { 
                /*等待任务完成*/
                Task.WaitAll(_CookTasks);
                swTask1.Stop();
                Console.WriteLine("不采用Lock操作,字符串长度:{0},耗时:{1}", AppendStrUnLock.Length, swTask1.ElapsedMilliseconds);
                /*释放资源*/
            });

            Console.ReadLine();
        }
    }


相关评论