C#多線程基礎


最近自己寫了個小爬蟲,里面用到了多線程技術,忽然發現對此技術竟然有些陌生了,於是乎開始瘋狂的去問度娘,在此記錄下來,以便自己和各位小伙伴們學習。

一、什么是線程

一個應用程序就相當於一個進程,進程擁有應用程序的所有資源進程包括線程,進程的資源被線程共享,但不擁有線程。我們可以打開電腦中的任務管理器,運行的.exe都是一個進程,里面的分支是線程。

二、多線程

多線程其實就是進程中一段並行運行的代碼

1. 創建並啟動線程

 1         static void Main()
 2         {
 3             //獲取線程Id
 4             var threadId = Thread.CurrentThread.ManagedThreadId;
 5             var thread = new Thread(Test1);
 6             thread.Start();
 7 
 8             Console.WriteLine(threadId + "_Main()");
 9             Console.Read();
10         }
11 
12         /// <summary>
13         /// 測試方法
14         /// </summary>
15         private static void Test1()
16         {
17             //獲取線程Id
18             var threadId = Thread.CurrentThread.ManagedThreadId;
19             Console.WriteLine(threadId + "_Test()");
20             for (int i = 0; i < 10; i++)
21             {
22                 Console.WriteLine(threadId + "_" + i);
23             }
24         }

結果:

 

 

 2、暫定線程諾干時間

 1         static void Main()
 2         {
 3             //獲取線程Id
 4             var threadId = Thread.CurrentThread.ManagedThreadId;
 5             var thread = new Thread(Test1);
 6             thread.Start();
 7             Console.WriteLine($"主線程Id{threadId}_Main()");
 8             Console.Read();
 9         }
10 
11         /// <summary>
12         /// 測試方法
13         /// </summary>
14         private static void Test1()
15         {
16             //獲取線程Id
17             var threadId = Thread.CurrentThread.ManagedThreadId;
18             Console.WriteLine($"輔線程Id{threadId}_Test()");
19             for (int i = 0; i < 10; i++)
20             {
21                 Thread.Sleep(1000);//單位毫秒
22                 Console.WriteLine($"輔線程Id{threadId}_{DateTime.Now}");
23             }
24         }

結果:

 

 

 3、線程合並

Thread.Join操作會阻塞當前線程,等待子線程完成后再進行運行。

 1         static void Main()
 2         {
 3             //獲取線程Id
 4             var threadId = Thread.CurrentThread.ManagedThreadId;
 5             var thread = new Thread(Test1);
 6             thread.Start();
 7             Console.WriteLine($"主線程Id{threadId}_Main()1");
 8             thread.Join();
 9             Console.WriteLine($"主線程Id{threadId}_Main()2");
10             Console.Read();
11         }
12 
13         /// <summary>
14         /// 測試方法
15         /// </summary>
16         private static void Test1()
17         {
18             //獲取線程Id
19             var threadId = Thread.CurrentThread.ManagedThreadId;
20             Console.WriteLine($"輔線程Id{threadId}_Test()");
21             for (int i = 0; i < 10; i++)
22             {
23                 Thread.Sleep(1000);//單位毫秒
24                 Console.WriteLine($"輔線程Id{threadId}_{DateTime.Now}");
25             }
26         }

結果:

 

 

 4、線程終止

 1         static void Main()
 2         {
 3             //獲取線程Id
 4             var threadId = Thread.CurrentThread.ManagedThreadId;
 5             var thread = new Thread(Test1);
 6             thread.Start();
 7             Console.WriteLine($"主線程Id{threadId}_Main()1");
 8             Thread.Sleep(3000);
 9            thread.Abort();
10             Console.WriteLine($"主線程Id{threadId}_Main()2");
11             Console.Read();
12         }
13 
14         /// <summary>
15         /// 測試方法
16         /// </summary>
17         private static void Test1()
18         {
19             //獲取線程Id
20             var threadId = Thread.CurrentThread.ManagedThreadId;
21             Console.WriteLine($"輔線程Id{threadId}_Test()");
22             for (int i = 0; i < 10; i++)
23             {
24                 Thread.Sleep(1000);//單位毫秒
25                 Console.WriteLine($"輔線程Id{threadId}_{DateTime.Now}");
26             }
27         }

結果:

 5、線程中的參數傳遞

 1 static void Main()
 2         {
 3             //獲取線程Id
 4             var threadId = Thread.CurrentThread.ManagedThreadId;
 5             Console.WriteLine($"主線程Id{threadId}_Main()");
 6             //第一種參數傳遞方式
 7             var thread1 = new Thread(() => Test1("小魔王"));
 8             thread1.Start();
 9 
10             //第二種參數傳遞方式(參數只能是一個,object類型)
11             var parameterizedThreadStart = new ParameterizedThreadStart(Test2);
12             var thread2 = new Thread(parameterizedThreadStart);
13             thread2.Start("大魔王");
14             Console.Read();
15         }
16 
17         /// <summary>
18         /// 測試方法
19         /// </summary>
20         private static void Test1(string name)
21         {
22             //獲取線程Id
23             var threadId = Thread.CurrentThread.ManagedThreadId;
24             Console.WriteLine($"輔線程Id{threadId}_我的名字叫:{name}");
25         }
26 
27         /// <summary>
28         /// 測試方法
29         /// </summary>
30         private static void Test2(object name)
31         {
32             //獲取線程Id
33             var threadId = Thread.CurrentThread.ManagedThreadId;
34             Console.WriteLine($"輔線程Id{threadId}_我的名字叫:{name}");
35         }

結果:

 

 

 還有其他的傳遞方式,在此先不做說明了,這里只介紹Thread提供的這么幾種。

6、線程安全和線程鎖Lock

線程安全就是多線程訪問時,采用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。線程安全情況下,不會出現數據不一致或者數據污染的問題。 線程不安全就是不提供數據訪問保護,有可能出現多個線程先后更改數據造成所得到的數據是臟數據! 若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。

lock 關鍵字通過獲取指定對象的互斥鎖,將語句塊標記為臨界區,執行語句然后釋放該鎖。

lock 確保當一個線程位於代碼的臨界區時,另一個線程不進入臨界區。如果其他線程試圖進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放。使用Lock,會導致整個應用程序串行化,降低程序的並發能力,影響性能。

到底什么場景下要使用lock保證線程安全:該串行就串行,該並行就並行。

加鎖前:

 1  public static int i = 1000000;
 2         static void Main()
 3         {
 4             //獲取線程Id
 5             var threadId = Thread.CurrentThread.ManagedThreadId;
 6             for (int j = 0; j < 2; j++)
 7             {
 8                 var thread = new Thread(Test1);
 9                 thread.Start();
10             }
11             Console.Read();
12         }
13 
14         /// <summary>
15         /// 測試方法
16         /// </summary>
17         private static void Test1()
18         {
19             //獲取線程Id
20             var threadId = Thread.CurrentThread.ManagedThreadId;
21            
22             Console.WriteLine($"輔線程Id{threadId}_i初始值:{i}");
23             int count = 0;
24             for (int j = 0; j < 1000000; j++)
25             {
26                 i--;
27                 count++;
28             }
29             Console.WriteLine($"輔線程Id{threadId}_運行次數:{count}");
30             Console.WriteLine($"輔線程Id{threadId}_i結束值:{i}");
31         }

結果:

 

 

 

加鎖后:

 1  public static int i = 1000000;
 2         private readonly static object objLock = new object();
 3         static void Main()
 4         {
 5             //獲取線程Id
 6             var threadId = Thread.CurrentThread.ManagedThreadId;
 7             for (int j = 0; j < 2; j++)
 8             {
 9                 var thread = new Thread(Test1);
10                 thread.Start();
11             }
12             Console.Read();
13         }
14 
15         private static void Test1()
16         {
17             //獲取線程Id
18             var threadId = Thread.CurrentThread.ManagedThreadId;
19            
20             int count = 0;
21             lock (objLock)
22             {
23                 Console.WriteLine($"輔線程Id{threadId}_i初始值:{i}");
24                 for (int j = 0; j < 1000000; j++)
25                 {
26                     i--;
27                     count++;
28                 }
29             }
30             Console.WriteLine($"輔線程Id{threadId}_運行次數:{count}");
31             Console.WriteLine($"輔線程Id{threadId}_i結束值:{i}");
32         }

結果:

 

好啦,今天關於線程的知識就分箱到這里啦。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM