多線程編程學習筆記——線程同步(一)

来源:http://www.cnblogs.com/chillsrc/archive/2017/10/27/7743930.html
-Advertisement-
Play Games

多線程使用共用變數,就會涉及到一個線程同步的問題。那如何解決呢? 1) 重構程式,移除多線程的共用變數,讓一個線程只訪問一個自有變數 2) 使用原子操作,一個操作只占用一個量子時間,一次完成,只有噹噹前操作完成之後,其他線程才能進行操作。這樣可以避免使用獨占鎖,避免死鎖。... ...


接上文 多線程編程學習筆記-基礎(一)

接上文 多線程編程學習筆記-基礎(二)

接上文 多線程編程學習筆記-基礎(三)

 

      就如上一篇文章(多線程編程學習筆記-基礎(三))中的示例代碼十,一樣如果多線程使用共用變數,就會涉及到一個線程同步的問題。那如何解決呢?

    方法有三:

1)        重構程式,移除多線程的共用變數,讓一個線程只訪問一個自有變數

2)        使用原子操作,一個操作只占用一個量子時間,一次完成,只有噹噹前操作完成之後,其他線程才能進行操作。這樣可以避免使用獨占鎖,避免死鎖。

3)        通過NET構架提供的Mutex、AutoRestEvent、CountDownEven、SpinWait等類,來進行線程間的同步。

 

一、使用InterLocked類

        在上一篇文章中,我們使用lock來解決多線程訪問帶來的問,而在這裡我們使用InterLocked類提供的原子操作,從而幫助我們不需要使用lock鎖來鎖定,以免造成死鎖。

       接下來我們改造一下上一篇文章中的代碼,代碼如下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //引入線程
using System.Diagnostics;
 

namespace ThreadSynchronousDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("開始,InterLocked 同步");
            var c = new Counter();
            Thread t = new Thread(() => Count(c));
            var t3 = new Thread(() => Count(c));

            var t2 = new Thread(() => Count(c));
            t.Name = "線程1"; 

            //啟動線程
            t.Start();
            t2.Name = "線程2";
            t2.Start();
 

            t3.Name = "線程3";
            t3.Start();
            t.Join();
            t2.Join();
            t3.Join();
            Console.WriteLine(string.Format("沒有加鎖的多線程總計:{0}", c.Count));
            Console.WriteLine("----------------------------");
            var c1 = new CounterInterLocked();
            var t4 = new Thread(() => Count(c1));
            t4.Name = "線程4";
 

            var t5 = new Thread(() => Count(c1));
            t5.Name = "線程5";
            var t6 = new Thread(() => Count(c1));

            t6.Name = "線程6";
            t4.Start();
            t5.Start();
            t6.Start();
            t4.Join();
            t5.Join();
            t6.Join();
            Console.WriteLine(string.Format("InterLocked的多線程總計:{0}", c1.Count));
            Console.Read();
        }

        static void Count(CountBase cnt)
        {
            for (int i = 0; i < 100000; i++)
            {
                cnt.Incerement();
                cnt.Dncerement();
            }
        }
    }

    abstract class CountBase
    {
        public abstract void Incerement();
        public abstract void Dncerement();
    }
    class Counter : CountBase
    {
        public int Count { get; private set; }
        public override void Dncerement()
        {
            Count--;
        }
        public override void Incerement()
        {
            Count++;
        }
    }

    class CounterInterLocked : CountBase
    {
        private int m_count;
        public int Count { get { return m_count; } }
        public override void Dncerement()
        {

            Interlocked.Decrement(ref m_count);
        }
        public override void Incerement()
        {

            Interlocked.Increment(ref m_count);
        }
    }
}

此程式運行結果如下圖,跟上一篇中的示例十,結果是一樣的。只是代碼上的區別。

 

 

二、使用Mutex類

1. 接下來我們來學習使用Mutex類來實現線程間的同步問題。

2. 在程式啟動時,設置InitialOwner為false,這表示如果互斥量已經建立,則允許程式獲取互斥量。如果沒有互斥量,則程式直接運行,等待接收任意鍵,然後釋放互斥量。

3.代碼如下: 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //引入線程
using System.Diagnostics;

namespace ThreadSynchronousDemo
{
    class Program
    {
        const string mutexName = "syncMutex";
        static void Main(string[] args)
        {
            Console.WriteLine("開始,Mutex 同步");
            using (var mut=new Mutex(false,mutexName))
            {
                if (!mut.WaitOne(TimeSpan.FromSeconds(5),false))
                {
                    Console.WriteLine("等待5秒之後運行。。。。");
                }
                else
                {
                    Console.WriteLine("正在運行。。。。,請輸入任意鍵");
                    Console.ReadLine();
                    mut.ReleaseMutex();
                    Console.WriteLine("釋放互斥量");

                }
            }         

            Console.Read();

        }
    }
}

 

4.運行結果如下圖。

 

        在debug目錄下,先運行主程式,如上圖中1,則程式1正常運行,此時如果再次打開Debug目錄下的應用主程式,則運行結果如上圖中2。說明互斥量起作用了。

5.先在上圖主程式1中輸入k,然後回車,結果如下圖中3。我們從Debug目錄下,再次打開應用程式,則應用程式的運行結果如下圖中4。說明主程式1,已經把互斥量釋放。

 

       註意:具名互斥量是全局的操作系統對象。請務必正確關閉互斥量。最好使用using來包裹互斥量代碼。這種方式可以在不同程式中同步線程。

 

三、使用SemaphoreSlim類

       SemaphoreSlim是Semaphore類的一個輕量級版本。此類限制了同時訪問同一資源的線程數量。

        在.net中,類Semaphore封裝了CLR中的內核同步對象。與標準的排他鎖對象(Monitor,Mutex,SpinLock)不同的是,它不是一個排他的鎖對象,它與SemaphoreSlim,ReaderWriteLock等一樣允許多個有限的線程同時訪問共用記憶體資源。

        Semaphore就好像一個柵欄,有一定的容量,當裡面的線程數量到達設置的最大值時候,就沒有線程可以進去。然後,如果一個線程工作完成以後出來了,那下一個線程就可以進去了。Semaphore的WaitOne或Release等操作分別將自動地遞減或者遞增信號量的當前計數值。當線程試圖對計數值已經為0的信號量執行WaitOne操作時,線程將阻塞直到計數值大於0。在構造Semaphore時,最少需要2個參數。信號量的初始容量和最大的容量。

 1.程式代碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //引入線程
using System.Diagnostics; 

namespace ThreadSynchronousDemo
{
    class Program
    {
        static SemaphoreSlim semapSlim = new SemaphoreSlim(5);
        static void Main(string[] args)
        {
            Console.WriteLine("開始,SemaphoreSlim 同步");
            for (int i = 1; i < 9; i++)
            {
                string threadName = "線程 " + i;
                int seconds = new Random().Next(1, 10);
                var t = new Thread((() => AccessDatabase(threadName, seconds)));
                t.Start();
            }
            Console.Read();
        }

        static void AccessDatabase(string name,int seconds)
        {
            Console.WriteLine("{0} 等待訪問資料庫", name);
            semapSlim.Wait();
            Console.WriteLine("{0} 被授予對資料庫的訪問許可權", name);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine("{0} 完成了", name);
            semapSlim.Release();          

        }
    }
}

2.程式運行結果如下圖。

 

       當程式啟動時,創建了一個SemaphoreSlim對象,併在構造函數中指定了併發的線程數量,然後啟動了10個不同名稱,不同初始運行時間的線程。

        每個線程都嘗試獲取資料庫訪問許可權,但是我們使用SemaphoreSlim對象做了限制,只有5個線程能同時訪問資料庫,當前5個線程獲取了資料庫訪問許可權之後,剩下的5個線程只能等待,直到有線程完成工作,並調用Semaphore的Release方法。


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

-Advertisement-
Play Games
更多相關文章
  • 前言作為一名合格的furry,我不僅要吸娜娜奇,還要天天泡在fa吸大觸們的furry作品,這其中難免遇到某個十分喜愛的作者,於是便想down空此作者的所有作品。鑒於一張張的保存實在費時費力,寫個爬蟲來解決眼前的問題似乎再好不過了,所以便有了現在這個下載器。功能介紹根據作者名批量下載此作者的所有作品,... ...
  • 這是今天幫 "檸檬" 分析一個 "AsyncLocal相關的問題" 時發現的. 試想這個代碼輸出的值是多少? 答案是123. 為什麼修改了 的值卻無效呢? 這要從AsyncLocal的運作機制說起. 首先這是 "AsyncLocal的源代碼" : 獲取和設置值用的是 和`ExecutionConte ...
  • WCF系統內置綁定列表 編碼格式 一個綁定,適用於與符合 WS-Basic Profile的Web服務(例如基於 ASP.NET Web 服務(ASMX)的服務)進行的通信。 此綁定使用HTTP作為傳輸協議,並使用文本/XML作為預設的消息編碼 Text, MTOM WCF各系統綁定所支持的功能 ...
  • 在 "上一篇" 我們對CoreCLR中的JIT有了一個基礎的瞭解, 這一篇我們將更詳細分析JIT的實現. JIT的實現代碼主要在 "https://github.com/dotnet/coreclr/tree/master/src/jit" 下, 要對一個的函數的JIT過程進行詳細分析, 最好的辦法 ...
  • ASP.NET Core 2.0 開源Git HTTP Server,實現類似 GitHub、GitLab。 GitHub:https://github.com/linezero/GitServer 設置 需要先安裝Git,並確保git 命令可以執行,GitPath 可以是 git 的絕對路徑。 目 ...
  • 一、關於分部視圖(Partial View) Partial View是可以應用在View中的,編寫一次,在其他View中可以被反覆使用。通常都是放在"Views——Shared"文件夾中。 1. 創建Partial View:右鍵"Views——Shared"文件夾添加分部視圖。 2. 使用Par ...
  • 最近有不少網友提起await和async,呵呵,C# 5引進的語法糖。 這個語法糖還真不好吃,能繞倒一堆初學的朋友,在網上也有很多網友關於這塊知識點的爭論,有對有錯,今天在這裡把這個誤區好好講講。 在await(C# 參考)這樣寫道: “await 運算符應用於非同步方法中的任務,在方法的執行中插入掛 ...
  • 一、概述 1. 程式集是.NET應用程式的部署單元 程式集是.NET應用程式的部署單元。 程式集是自我描述的安裝單元,由一個或多個文件組成。 通常擴展名是EXE或DLL的.NET可執行程式稱為程式集。 .NET程式集包含元數據。 2. 程式集的特性 程式集是自我描述的。 版本的相互依賴性在程式集的清 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...