多線程(線程同步1)

来源:http://www.cnblogs.com/yonghuacui/archive/2016/12/21/6206270.html
-Advertisement-
Play Games

在多線程(線程同步)中,我們將學習多線程中操作共用資源的技術,學習到的知識點如下所示: 執行基本的原子操作 使用Mutex構造 使用SemaphoreSlim構造 使用AutoResetEvent構造 使用ManualResetEventSlim構造 使用CountDownEvent構造 使用Bar ...


  在多線程(線程同步)中,我們將學習多線程中操作共用資源的技術,學習到的知識點如下所示:

  • 執行基本的原子操作
  • 使用Mutex構造
  • 使用SemaphoreSlim構造
  • 使用AutoResetEvent構造
  • 使用ManualResetEventSlim構造
  • 使用CountDownEvent構造
  • 使用Barrier構造
  • 使用ReaderWriterLockSlim構造
  • 使用SpinWait構造

 一、執行基本的原子操作

  在這一小節中,我們將學習如何在沒有阻塞線程(blocking threads)發生的情況下,在一個對象上執行基本的原子操作並能阻止競爭條件(race condition)的發生。操作步驟如下所示:

1、使用Visual Studio 2015創建一個新的控制台應用程式。

2、雙擊打開“Program.cs”文件,編寫代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 
 5 namespace Recipe01
 6 {
 7     abstract class CounterBase
 8     {
 9         public abstract void Increment();
10 
11         public abstract void Decrement();
12     }
13 
14     class Counter : CounterBase
15     {
16         private int count;
17 
18         public int Count => count;
19 
20         public override void Increment()
21         {
22             count++;
23         }
24 
25         public override void Decrement()
26         {
27             count--;
28         }
29     }
30 
31     class CounterNoLock : CounterBase
32     {
33         private int count;
34 
35         public int Count => count;
36 
37         public override void Increment()
38         {
39             Interlocked.Increment(ref count);
40         }
41 
42         public override void Decrement()
43         {
44             Interlocked.Decrement(ref count);
45         }
46     }
47 
48     class Program
49     {
50         static void TestCounter(CounterBase c)
51         {
52             for (int i = 0; i < 100000; i++)
53             {
54                 c.Increment();
55                 c.Decrement();
56             }
57         }
58 
59         static void Main(string[] args)
60         {
61             WriteLine("Incorrect counter");
62 
63             var c1 = new Counter();
64 
65             var t1 = new Thread(() => TestCounter(c1));
66             var t2 = new Thread(() => TestCounter(c1));
67             var t3 = new Thread(() => TestCounter(c1));
68             t1.Start();
69             t2.Start();
70             t3.Start();
71             t1.Join();
72             t2.Join();
73             t3.Join();
74 
75             WriteLine($"Total count: {c1.Count}");
76             WriteLine("--------------------------");
77 
78             WriteLine("Correct counter");
79 
80             var c2 = new CounterNoLock();
81 
82             t1 = new Thread(() => TestCounter(c2));
83             t2 = new Thread(() => TestCounter(c2));
84             t3 = new Thread(() => TestCounter(c2));
85             t1.Start();
86             t2.Start();
87             t3.Start();
88             t1.Join();
89             t2.Join();
90             t3.Join();
91 
92             WriteLine($"Total count: {c2.Count}");
93         }
94     }
95 }

3、運行該控制台應用程式,運行效果(每次運行效果可能不同)如下圖所示:

  在第63行代碼處,我們創建了一個非線程安全的Counter類的一個對象c1,由於它是非線程安全的,因此會發生競爭條件(race condition)。

  在第65~67行代碼處,我們創建了三個線程來運行c1對象的“TestCounter”方法,在該方法中,我們按順序對c1對象的count變數執行自增和自減操作。由於c1不是線程安全的,因此在這種情況下,我們得到的counter值是不確定的,我們可以得到0值,但多運行幾次,多數情況下會得到不是0值得錯誤結果。

  在多線程(基礎篇)中,我們使用lock關鍵字鎖定對象來解決這個問題,但是使用lock關鍵字會造成其他線程的阻塞。但是,在本示例中我們沒有使用lock關鍵字,而是使用了Interlocked構造,它對於基本的數學操作提供了自增(Increment)、自減(Decrement)以及其他一些方法。

二、使用Mutex構造

   在這一小節中,我們將學習如何使用Mutex構造同步兩個單獨的程式,即進程間的同步。具體步驟如下所示:

1、使用Visual Studio 2015創建一個新的控制台應用程式。

2、雙擊打開“Program.cs”文件,編寫代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 
 5 namespace Recipe02
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             const string MutexName = "Multithreading";
12 
13             using (var m = new Mutex(false, MutexName))
14             {
15                 // WaitOne方法的作用是阻止當前線程,直到收到其他實例釋放的處理信號。
16                 // 第一個參數是等待超時時間,第二個是否退出上下文同步域。
17                 if (!m.WaitOne(TimeSpan.FromSeconds(10), false))
18                 {
19                     WriteLine("Second instance is running!");
20                     ReadLine();
21                 }
22                 else
23                 {
24                     WriteLine("Running!");
25                     ReadLine();
26                     // 釋放互斥資源
27                     m.ReleaseMutex();
28                 }
29             }
30 
31             ReadLine();
32         }
33     }
34 }

3、編譯代碼,執行兩次該程式,運行效果如下所示:

第一種情況的運行結果:

第二種情況的運行結果:

  在第11行代碼處,我們定義了一個mutex(互斥量)的名稱為“Multithreading”,併在第13行代碼處將其傳遞給了Mutex類的構造方法,該構造方法的第一個參數initialOwner我們賦值為false,這允許程式獲得一個已經被創建的mutex。如果沒有任何線程鎖定互斥資源,程式只簡單地顯示“Running”,然後等待按下任何鍵以釋放互斥資源。

  如果我們啟動該程式的第二個實例,如果在10秒內我們沒有在第一個實例下按下任何按鈕以釋放互斥資源,那麼在第二個實例中就會顯示“Second instance is running!”,如第一種情況的運行結果所示。如果在10內我們在第一個實例中按下任何鍵以釋放互斥資源,那麼在第二個實例中就會顯示“Running”,如第二種情況的運行結果所示。

三、使用SemaphoreSlim構造

  在這一小節中,我們將學習如何在SemaphoreSlim構造的幫助下,限制同時訪問資源的線程數量。具體步驟如下所示:

1、使用Visual Studio 2015創建一個新的控制台應用程式。

2、雙擊打開“Program.cs”文件,編寫代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe03
 7 {
 8     class Program
 9     {
10         static SemaphoreSlim semaphore = new SemaphoreSlim(4);
11 
12         static void AccessDatabase(string name, int seconds)
13         {
14             WriteLine($"{name} waits to access a database");
15             semaphore.Wait();
16             WriteLine($"{name} was granted an access to a database");
17             Sleep(TimeSpan.FromSeconds(seconds));
18             WriteLine($"{name} is completed");
19             semaphore.Release();
20         }
21 
22         static void Main(string[] args)
23         {
24             for(int i = 1; i <= 6; i++)
25             {
26                 string threadName = "Thread" + i;
27                 int secondsToWait = 2 + 2 * i;
28                 var t = new Thread(() => AccessDatabase(threadName, secondsToWait));
29                 t.Start();
30             }
31         }
32     }
33 }

3、運行該控制台應用程式,運行效果(每次運行效果可能不同)如下圖所示:

 

  在第10行代碼處,我們創建了一個SemaphoreSlim的實例,並對該構造方法傳遞了參數4,該參數指定了可以有多少個線程同時訪問資源。然後,我們啟動了6個不同名字的線程。每個線程都試著獲取對資料庫的訪問,但是,我們限制了最多只有4個線程可以訪問資料庫,因此,當4個線程訪問資料庫後,其他2個線程必須等待,直到其他線程完成其工作後,調用“Release”方法釋放資源之後才能訪問資料庫。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近學習使用CodeSmith代碼生成器 CodeSmith 是一種語法類似於asp.net的基於模板的代碼生成器,程式可以自定義模板,從而減少重覆編碼的勞動量,提高效率。 作用:CodeSmith 是一種基於模板的代碼生成工具,它使用類似於ASP.NET的語法來生成任意類型的代碼或文本。與其他許多 ...
  • 迴圈、邏輯語句塊 好久不寫博客了,斷更了好幾天了,從上周五到今天,從北京到上海,跨越了1213.0公裡,從一個熟悉的城市到陌生的城市,還好本人適應力比較好,還有感謝小伙伴的接風咯,一切都不是事,好了,進入正題: 本篇還是.NET 基礎部分咯,主要簡述迴圈,判斷: 迴圈: for迴圈 語法: for( ...
  • 一、借鑒說明 1.《Head First Design Patterns》(中文名《深入淺出設計模式》) 2.維基百科,策略模式,https://zh.wikipedia.org/wiki/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F 二、策略模式 基礎知識 將一類相同的... ...
  • "檢索COM類工廠中 CLSID為 {00024500-0000-0000-C000-000000000046}的組件時失敗,原因是出現以下錯誤: 80070005" 問題的解決 ...
  • 菜單收縮有很多種方法具體如何實現還是看個人想法: 第一種通過後臺控制收起與展開: 效果圖: 代碼 : <Grid> <Grid.ColumnDefinitions> <ColumnDefinition x:Name="cd" Width="154"/> <ColumnDefinition /> </ ...
  • 用html實現圖片上傳 後臺採用.net其中在這裡要借用一個js插件 在這裡我會寫一個圖片上傳的一個小Demo,有不全的地方多多包容,和提議, 我把已經寫好的demo已經上傳到百度雲 在這裡可以下載 http://pan.baidu.com/s/1bG2934 開始 html中的內容是 <body> ...
  • 年關將至,對於大部分程式員來說,馬上就可以閑下來一段時間了,然而在這個閑暇的時間里,唯有爭論哪門語言更好可以消磨時光,估計最近會有很多關於java與.net的博文出現,我表示要作為一個吃瓜群眾,靜靜的看著大佬們發表心情。 以上的廢話說的夠多了,這裡就不再廢話了,還是切入正題吧。 在項目開發中,對於系 ...
  • 本文要介紹的是ASP.NET怎樣讀寫文本文件,但更重要的是實現的過程。使用的工具是Visual Studio 2015 ,.NET版本是4.6.1 。一共建立的2個項目,HoverTreePanel和HoverTreeWeb,都是ASP.NET項目。文章末尾附源碼下載。項目結果如下圖:讀寫文件功能在 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...