线程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的对象不许是指同一个对象,否则不起作用。

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

那些曾以为念念不忘的事情就在我们念念不忘的过程中,被我们遗忘了。
原文地址:https://www.cnblogs.com/niuww/p/3027905.html