• 【总结】C# 线程同步技术(一)之 Join 方法


    最近工作闲暇之际,翻阅了以前保存的电子书《C#多线程编程手册》,发现此书同步技术这块写的甚好,于是参考此书并结合实例,对同步技术做一下总结和分析,也算是读书笔记与心得体会吧,并与大家分享。

    书中提到的同步技术有很多种,归纳起来常用的方式有以下几种:

    1、利用属性标签方式进行方法同步和上下文同步:MethodImplAttribute 类SynchronizationAttribute 类
    2、同步代码区:Monitor 类Lock 关键字ReaderWriterLock 类
    3、手控同步:AutoResetEvent 类ManualResetEvent 类Mutex 类Interlocked 类

    后面的文章我们会依次对以上类或关键字进行介绍,首先我们先来说说Thread类中的Join方法,书中对它的介绍比较简略,但是我觉得它也算是线程间同步方式的一种了,对它的用法也来总结和归纳一下。 

    MSDN对 Join 方法解释说:在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止或经过了指定时间为止。

    哈哈,MSDN的解释永远都是那么的绕嘴,我们还是通过一个简单的控制台程序作为例子来说明一下吧:

    首先我们建立一个计算类Calculate,里面包含一个加法线程 ThreadAdd 和一个加法方法,在Add()方法中并让执行运算的线程休眠5秒,代码如下: 

    //计算类
    public class Calculate
        {
            public Thread threadAdd;
            
            public Calculate()
            {
                threadAdd = new Thread(new ThreadStart(Add));            
            }
    
            //加法运算
            public void Add()
            {
                Console.ForegroundColor = ConsoleColor.Blue;
                Console.WriteLine("进入加法计算"); 
                Thread.Sleep(5000); Console.ForegroundColor
    = ConsoleColor.Blue; Console.WriteLine("加法运算结果: x={0} y={1} x+y={2}", 1, 2, 1 + 2); } }

     然后我们在Main方法中进行调用:

    class Program
        {
            static void Main(string[] args)
            {
                Calculate calculate = new Calculate();
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("主线程输出:准备进行加法运算:");
                calculate.threadAdd.Start();
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("主线程输出:运算完毕");
                Console.ReadKey();
            }
        }

     运算结果如图:

     到这里,我们会发现执行Main() 方法的主线程,并没有等待 执行加法的工作线程,而是直接输出了“运算完毕”,这时候我们的Join() 方法就该上场了,我们对Main() 函数进行修改一下:

     static void Main(string[] args)
            {
                Calculate calculate = new Calculate();
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("主线程输出:准备进行加法运算:");
                calculate.threadAdd.Start();
                //增加Join
                calculate.threadAdd.Join();
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("主线程输出:运算完毕");
                Console.ReadKey();
            }

     运行结果如下图:

    这样的结果是我们想要的正常输出顺序。运行的时候我们发现,当主线程执行到 calculate.threadAdd.Join(); 的时候,并没有继续执行,一直等到 加法线程 运算完毕之后主线程才继续运行,这不就是和MSDN中解释的一样吗?主线程现在就属于调用线程,当主线程调用了calculate.threadAdd.Join()的时候,就发生了阻塞,直到加法线程运行完毕之后,才继续运行。

    现在我们在来看看Join的另外两个重载方法:Join(Int32) 和 Join(TimeSpan),这两个方法其实是一样的,输入参数说白了就是设置阻塞的等待时间,返回值是bool类型,如果线程已终止,则为 true,否则返回 false 。不明白没关系,我们继续来看例子:

    我们修改一计算类,再增加一个 减法方法Sub() 和一个执行减法的线程ThreadSub,代码如下:

    //计算类
        public class Calculate
        {
            public Thread threadAdd;
            public Thread threadSub;
            public Calculate()
            {
                threadAdd = new Thread(new ThreadStart(Add));
                threadSub = new Thread(new ThreadStart(Sub));
            }
    
            //加法运算
            public void Add()
            {
                Console.ForegroundColor = ConsoleColor.Blue;
                Console.WriteLine("进入加法计算");
                Thread.Sleep(5000);
                Console.ForegroundColor = ConsoleColor.Blue;
                Console.WriteLine("加法运算结果: x={0} y={1} x+y={2}", 1, 2, 1 + 2);
            }
    
            //新增减法运算 
            public void Sub()
            {
                //主要是这里
                bool b = threadAdd.Join(1000);
                Console.ForegroundColor = ConsoleColor.Red;
                if (b)
                {
                    Console.WriteLine("加法运算已经完成,进入减法法计算");
                }
                else
                {
                    Console.WriteLine("加法运算超时,先进入减法法计算");
                }
    
                Thread.Sleep(2000);
                Console.WriteLine("进入减法运算");
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("减法运算结果: x={0} y={1} x-y={2}", 10, 2, 10 - 2);
            }
        }

     Main() 方法修改为:

    class Program
        {
            static void Main(string[] args)
            {
                Calculate calculate = new Calculate();
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("主线程输出:准备进行加法和减法两种运算:");
    
                calculate.threadAdd.Start();
                calculate.threadSub.Start();
                calculate.threadAdd.Join();
                calculate.threadSub.Join();
    
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("主线程输出:所有运算完毕");
                Console.ReadKey();
            }
        }

     运行结果如下:

    结果是正确的,我们来分析一下整个的运算过程:

    首先,主线程遇到 calculate.threadAdd.Join();  和 calculate.threadSub.Join(); 肯定会发生阻塞,等待这两个线程完成后,才会继续执行,这个不容质疑。然后我们看加法线程和减法线程,这两个线程几乎同时执行,谁先执行,我们是不可预期的。比如先执行加法线程,当执行到Thread.Sleep(5000),的时候,加法线程休眠5s,减法线程由于调用了 threadAdd.Join(1000); 所以减法线程会阻塞1s ,1s 之后由于加法线程还没有执行完成,所以 返回值为 false,减法线程继续执行,减法线程执行完毕后,又过了一会,加法线程才继续执行。这样就会得出我们上面的运行结果。

    Thread.Join() 方法的用法这么多了,下一篇 来总结和说明一下  MethodImplAttribute 类 和 SynchronizationAttribute 类。

    本 文源 码 下 载

    作者:Rising Sun
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    [ACM_模拟] ZJUT 1155 爱乐大街的门牌号 (规律 长为n的含k个逆序数的最小字典序)
    [ACM_搜索] ZOJ 1103 || POJ 2415 Hike on a Graph (带条件移动3盘子到同一位置的最少步数 广搜)
    [ACM_动态规划] POJ 1050 To the Max ( 动态规划 二维 最大连续和 最大子矩阵)
    [ACM_搜索] POJ 1096 Space Station Shielding (搜索 + 洪泛算法Flood_Fill)
    [JAVA] java_实例 获得系统字体
    [JAVA] java仿windows 字体设置选项卡
    [JAVA] 一个可以编辑、编译、运行Java简单文件的记事本java实现
    [ACM_模拟] POJ 1094 Sorting It All Out (拓扑排序+Floyd算法 判断关系是否矛盾或统一)
    JS的数组相关知识
    JS的join方法
  • 原文地址:https://www.cnblogs.com/lxblog/p/2833974.html
Copyright © 2020-2023  润新知