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

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

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

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

采用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;
                        lock (o)
                        {
                            AppendStrLock.Append(str);
                        }
                    });
                }, task_index);
            }

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

            Console.ReadLine();
        }
    }

采用互斥锁代码下:

    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; 
                        bool lockTaken = false;
                        try
                        {
                            Monitor.Enter(o, ref lockTaken);
                            AppendStrMonitorLock.Append(str);
                        }
                        finally
                        {
                            if (lockTaken)
                                Monitor.Exit(o);
                        }
                    });
                }, task_index);
            }

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

            Console.ReadLine();
        }
    }

System.Threading.Monitor 类通过使用互斥锁提供了对象的同步访问机制,使用Lock关键字的等价代码使用起来更加简洁,不需要额外的异常捕获和处理代码。

但是System.Threading.Monitor好处是提供了些其他的方法(Lock中却没有),通过这些方法可以对锁的过程有更多的控制。需要注意的是 Lock关键字和System.Threading.Monitor类仍然是提供互斥访问的首选方法,不过在某些情形下,其他互斥锁原语可能会提供更好的性能和更小的开销,如SpinLock,Lock和System.Threading.Monitor类智能锁定对象,即引用类型。

锁超时 Monitor.TryEnter(o, 2000, ref lockTaken);

在多任务中,很多任务试图获得锁从而进入临界区,如果其中一个参与者不能释放锁,那么其他所有的任务都要在Monitor.Enter的方法内永久的等待下去。Monitor.TryEnter方法则提供了超时机制,如代码所示:

    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) =>
                {
                    try
                    {
                        Parallel.For(1, 200000, (i) =>
                        {
                            string str = "append message " + i;
                            bool lockTaken = false;
                            try
                            {
                                Monitor.TryEnter(o, 2000, ref lockTaken);
                                if (!lockTaken)
                                {
                                    throw new  OperationCanceledException("锁超时....");
                                }
                                if (i == 2)
                                {
                                    Thread.Sleep(40000);
                                }
                                AppendStrMonitorLock.Append(str);
                            }
                            catch (Exception ex)
                            {
                                throw ex;
                            }
                            finally
                            {
                                if (lockTaken)
                                    Monitor.Exit(o);
                            }
                        });
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }

                }, task_index);
            }

            /*ContinueWhenAll 提供一组任务完成后 延续方法*/
            var finalTask = Task.Factory.ContinueWhenAll(_CookTasks, (tasks) =>
            {
                /*等待任务完成*/
                Task.WaitAll(_CookTasks);
                swTask1.Stop();
                foreach (Task task in _CookTasks)
                {
                    if (task.Exception != null)
                    {
                        /*任务执行完成后  输出所有异常 打印异常报表*/
                        foreach (Exception exception in task.Exception.InnerExceptions)
                        {
                            Console.WriteLine("异常信息:{0}", exception.Message);
                        }
                    }
                }

                Console.WriteLine("不采用Lock操作,字符串长度:{0},耗时:{1}", AppendStrMonitorLock.Length, swTask1.ElapsedMilliseconds);
                /*释放资源*/
            });

            Console.ReadLine();
        }
    }

需要注意,上述代码中,异常并没有被捕捉到,因此每一个不能获得锁的任务都会出错退出并停止执行。

System.Threading.Monitor类还提供了以下三个方法,大家可以参考MSND:

自旋锁 - System.Threading.SpinLock

如果持有锁的时间非常短,锁的粒度很精细,那么自旋锁可以获得比其他锁机制更好的性能,互斥锁System.Threading.Monitor的开销非常大。

下述代码展现System.Threading.Monitor和System.Threading.SpinLock的性能:

    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)
        { 
            SpinLock sl=new SpinLock();
            _CookTasks = new Task[_particpants];
            Thread.Sleep(4000);
            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, 200000, (i) =>
                    {
                        string str = "append message " + i; 
                        bool lockTaken = false;
                        try
                        {
                            Monitor.Enter(o, ref lockTaken);
                            AppendStrMonitorLock.Append(str);
                        }
                        finally
                        {
                            if (lockTaken)
                                Monitor.Exit(o);
                        }
                    });
                }, task_index);
            }

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

            Console.ReadLine();
        }
    }


相关评论