zoukankan      html  css  js  c++  java
  • 线程Thread基础学习(1)

            学习过操作系统的人员对于线程一词并不陌生,或多或少或深或浅都有了解,但对于程序员来说,只有了解是不行的,在应聘工作的面试中或多或少总有面试官提到这些问题,此问题涉及领域并不宽,但作用着实不小,特别是在系统性能方面。在多核处理器盛行的今天,多线程成为面试官比较喜欢的话题。方式多为并发的理解,多线程的同步等等。

            为了能在工作有能有立足之地,程序员必须每天学习,不断学习新技术,新知识等等一切关于开发的新知识。每天不必学太多,只要有进步就行。为了不让自己的学习过手就忘,我把学习过程记录在这里,也是能把自己的学习经历,遇到的问题和大家分享讨论,有错误的地方还请大家指出,多多指教,目的是做到基础知识一步一个脚印,稳步前行。好了,不多说了,步入正题。

    以下定义一个测试线程类:

     1 using System;
     2 using System.Threading;
     3 
     4 namespace Demo.ProcessThread
     5 {
     6     public class TestThread
     7     {
     8         //静态引用类型字段
     9         static object obj = new object();
    10         // 实例引用类似字段
    11         object obj2 = new object();
    12         //静态方法
    13         public static void TestNoLock1()
    14         {
    15                 for (int i = 0; i < 10; i++)
    16                 {
    17                     Console.WriteLine(string.Format("TestNoLock1:{0}", i));
    18                 }
    19         }
    20         public static void TestNoLock2()
    21         {
    22                 for (int i = 0; i < 10; i++)
    23                 {
    24                     Console.WriteLine(string.Format("TestNoLock2:{0}", i));
    25                 }
    26         }
    27     }
    28 }

    在这里分情况讨论:

          1.没有使用lock关键字情况,线程之间没有同步信息。

     1 namespace Demo.ProcessThread
     2 {
     3     class Program
     4     {
     5         static void Main(string[] args)
     6         {
     7             ThreadStart ts1 = new ThreadStart(TestThread.TestNoLock1);
     8             Thread t1 = new Thread(ts1);
     9             ThreadStart ts2 = new ThreadStart(TestThread.TestNoLock2);
    10             Thread t2 = new Thread(ts2);
    11             t1.Start();
    12             t2.Start();
    13         }
    14     }
    15 }

     第一次执行结果:

    TestNoLock2:0
    TestNoLock2:1
    TestNoLock2:2
    TestNoLock2:3
    TestNoLock2:4
    TestNoLock1:0
    TestNoLock1:1
    TestNoLock1:2
    TestNoLock1:3
    TestNoLock1:4
    TestNoLock1:5
    TestNoLock1:6
    TestNoLock1:7
    TestNoLock1:8
    TestNoLock1:9
    TestNoLock2:5
    TestNoLock2:6
    TestNoLock2:7
    TestNoLock2:8
    TestNoLock2:9
    请按任意键继续. . .   

    第二次执行结果:

    TestNoLock2:0
    TestNoLock2:1
    TestNoLock2:2
    TestNoLock2:3
    TestNoLock2:4
    TestNoLock2:5
    TestNoLock2:6
    TestNoLock2:7
    TestNoLock2:8
    TestNoLock1:0
    TestNoLock1:1
    TestNoLock1:2
    TestNoLock1:3
    TestNoLock1:4
    TestNoLock1:5
    TestNoLock1:6
    TestNoLock1:7
    TestNoLock1:8
    TestNoLock1:9
    TestNoLock2:9
    请按任意键继续. . .

           两次结果完全不同,也没有规律可循,这就是线程的并发。多个线程之间如果不做任何处理,它们的执行时互不干扰的,所以结果每次输出都不同。这一步没有错,但是如果是多个线程共享同一个变量或存储空间,大家想想如果线程之间不做控制会出现什么情况?改造一下TestNoLock1和TestNoLock2方法,看结果:

     1         //静态字段
     2       public static int count = 0;
     3         public static void TestNoLock1()
     4         {
     5             for (int i = 0; i < 10; i++)
     6             {
     7                 count++;
     8                 Console.WriteLine(TestThread.count);
     9             }
    10         }
    11         public static void TestNoLock2()
    12         {
    13             for (int i = 0; i < 10; i++)
    14             {
    15                 count--;
    16                 Console.WriteLine(TestThread.count);
    17             }
    18         }

    测试:

    1 ThreadStart ts1 = new ThreadStart(TestThread.TestNoLock1);
    2             Thread t1 = new Thread(ts1);
    3             ThreadStart ts2 = new ThreadStart(TestThread.TestNoLock2);
    4             Thread t2 = new Thread(ts2);
    5             t1.Start();
    6             t2.Start();

    执行结果:

    1
    1
    2
    3
    0
    3
    2
    1
    0
    -1
    -2
    -3
    -4
    -5
    4
    -4
    -3
    -2
    -1
    0
    请按任意键继续. . .

           这肯定不是你想要的结果,并且每次执行结果还不相同。为了得到正确的结果,必须控制并发的执行,这时候lock关键字就要粉墨登场了(方法很多,这里只讨论lock,后续的会陆续介绍其他方法),好了,看代码,继续改造TestNoLock1和TestNoLock2方法:

     1  //静态引用类型字段
     2        static object obj = new object();
     3         public static void TestNoLock1()
     4         {
     5             lock (obj)
     6             {
     7                 for (int i = 0; i < 10; i++)
     8                 {
     9                     count++;
    10                     Console.WriteLine(TestThread.count);
    11                 }
    12             }
    13         }
    14         public static void TestNoLock2()
    15         {
    16             lock (obj)
    17             {
    18                 for (int i = 0; i < 10; i++)
    19                 {
    20                     count--;
    21                     Console.WriteLine(TestThread.count);
    22                 }
    23             }
    24         }

    再看执行结果:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    9
    8
    7
    6
    5
    4
    3
    2
    1
    0
    请按任意键继续. . .

           怎么样,是想要的结果吧,这就是lock的作用,lock可以同步多线程之间共享信息,当lock锁定一个对象时,其他进程在进行lock操作,就是发生线程阻塞,知道lock对象被释放,才会继续执行。

    接下来再看lock锁定的对象的特征:

         1.假如把obj的类型换成值类型(int,datetime)等,会发现有语法错误提示,“xx不是lock语句要求引用类型”,

         归纳:lock对象必须是引用类型,因为引用类型和值类型传值过程不同,引用类型传递地址,值类型传递副本,故不能作为Lock对象。

         2.在TestThread类中在定义两个实例方法,代码如下:

     1         //静态引用类型字段
     2         static object obj = new object();        
     3         // 实例方法
     4         public  void Test1()
     5         {
     6             // lock 必须锁定引用类型参数
     7             lock (obj)
     8             {
     9                 for (int i = 0; i < 10; i++)
    10                 {
    11                     Console.WriteLine(string.Format("Test1:{0}", i));
    12                 }
    13             }
    14         }
    15         public void Test2()
    16         {
    17             // lock 必须锁定引用类型参数
    18             lock (obj)
    19             {
    20                 for (int i = 0; i < 10; i++)
    21                 {
    22                     Console.WriteLine(string.Format("Test2:{0}", i));
    23                 }
    24             }
    25         }

    测试代码:

    1 ThreadStart tStart = new ThreadStart(new TestThread().Test1);
    2             Thread thread1 = new Thread(tStart);
    3 
    4             ThreadStart tStart2 = new ThreadStart(new TestThread().Test2);
    5             Thread thread2 = new Thread(tStart2);
    6             thread1.Start();
    7           thread2.Start();

    测试结果:多次执行结果相同,也是正确的执行结果:
    Test1:0
    Test1:1
    Test1:2
    Test1:3
    Test1:4
    Test1:5
    Test1:6
    Test1:7
    Test1:8
    Test1:9
    Test2:0
    Test2:1
    Test2:2
    Test2:3
    Test2:4
    Test2:5
    Test2:6
    Test2:7
    Test2:8
    Test2:9
    请按任意键继续. . .

    接着改造lock锁定对象为实例字段,把static关键字取消,代码如下:

    1 //静态引用类型字段
    2          object obj = new object();

    再看执行结果:

    Test2:0
    Test2:1
    Test2:2
    Test2:3
    Test2:4
    Test2:5
    Test2:6
    Test1:0
    Test1:1
    Test1:2
    Test1:3
    Test1:4
    Test1:5
    Test1:6
    Test1:7
    Test1:8
    Test1:9
    Test2:7
    Test2:8
    Test2:9
    请按任意键继续. . .

    顺序全乱了,并且每次执行的结果无法预期的。那么如何才能得到正确的结果呢?继续往下看,把测试代码改为:

    1             TestThread testThread = new TestThread();
    2             ThreadStart tStart = new ThreadStart(testThread.Test1);
    3             Thread thread1 = new Thread(tStart);
    4 
    5             ThreadStart tStart2 = new ThreadStart(testThread.Test2);
    6             Thread thread2 = new Thread(tStart2);
    7             thread1.Start();
    8             //thread1.Join();  // 可以确保thread1线程全部执行完毕,否则无限期阻塞
    9             thread2.Start();

    看看结果是不是有顺序了,归纳:不同的进程lock的对象不许是指同一个对象,否则不起作用。

    好了,这篇文章就先写到这里。

    那些曾以为念念不忘的事情就在我们念念不忘的过程中,被我们遗忘了。
  • 相关阅读:
    mysql修改加密方式
    信息安全学习路线
    DNS域传送漏洞
    CSRF漏洞
    反序列化漏洞
    计算机通讯基础
    gorm gen使用
    golang makefile使用
    linux命令行录制
    go代码自动生成注释
  • 原文地址:https://www.cnblogs.com/niuww/p/3027905.html
Copyright © 2011-2022 走看看