主頁 > .NET開發 > 一個lock鎖就可以分出低中高水平的程式員對問題的處置方式

一個lock鎖就可以分出低中高水平的程式員對問題的處置方式

2020-09-15 23:55:13 .NET開發

說到lock鎖,我相信在座的各位沒有不會用的,而且還知道怎么用不會出錯,但讓他們聊一聊為什么可以鎖住,都說人以群分,大概就有了下面低中高水平的三類人吧,

第一類人

將lock物件定義成static,這樣就能讓多個執行緒看到同一個物件,以此實作執行緒間互斥和保證同步,如果再深問為什么?就怕遮遮掩掩的說好像每個實體都有一個同步塊索引,再展開的話就頂不住了,反正大家都這么寫,我也不敢問,我也不會說,如果上代碼,只能這樣丟給你,


    public class Program
    {
        public static object lockMe = new object();

        public static void Main(string[] args)
        {
            var task1 = Task.Factory.StartNew(() =>
            {
                lock (lockMe)
                {
                    //todo
                }
            });

            var task2 = Task.Factory.StartNew(() =>
            {
                lock (lockMe)
                {
                    //todo
                }
            });

            Task.WaitAll(task1, task2);
        }
    }

第二類人

這類人可能看過CLR via C# 這樣類似圣經級著作,而且對相關概念也比較清楚,

1. 清楚‘參考型別’ 在堆上的布局結構及堆疊上的指標是指向方法表索引(型別物件指標),如下圖,

2. 清楚當lock住物件后,它的‘同步塊索引’ 和 CLR上的‘同步塊陣列’是呈現一個關聯關系,然后又是一張圖,

牛X點: 僅僅用了兩張圖就把這個事情解決的相當完美,讀者一看就明白了,然來是每個執行緒在lock的時候會查看一下物件的同步塊索引所映射的同步塊陣列中的坑中資訊來判斷是否可以加鎖,

不足點: 一定要挑刺的話,那就是這類人只是在聽別人講故事,到底是不是真的如此其實自己心里也沒譜,只是一味的相信對方的人格魅力,而真正????的人,十句話中只有一句假話~??????

第三類人

這類人就會動用資源或者人脈親自嘗試一下是不是如第二類人所描述的那樣,操刀的話,最好的工具就是windbg,接下來我就操刀一把,

1. 對‘參考型別’布局結構的補充

現在大家也知道了每個物件都有兩個額外開銷,就是‘同步塊索引’ + '方法表索引',在x86系統中,每個索引各占4位元組,而在x64系統中,每個索引各占8位元組,因我的系統是x64,按照x64版本測驗,

2. 案例代碼

有了上面的知識補充,接下來我開兩個task,在task中進行lock操作,


namespace ConsoleApp2
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var employee = new Employee();

            Console.WriteLine("步驟一:lock前!!!");
            Console.ReadLine();

            var task1 = Task.Factory.StartNew(() =>
            {
                lock (employee)
                {
                    Console.WriteLine("步驟二:lock1中,,,,");
                    Console.ReadLine();
                }
                Console.WriteLine("步驟二:退出lock1...");
            });

            var task2 = Task.Factory.StartNew(() =>
            {
                lock (employee)
                {
                    Console.WriteLine("步驟二:lock2中,,,,");
                    Console.ReadLine();
                }
                Console.WriteLine("步驟二:退出lock2...");
            });

            Task.WaitAll(task1, task2);
            Console.WriteLine("步驟三: lock后,全部退出!");
            Console.ReadLine();
        }
    }

    public class Employee
    {
        public int a = 1;
        public int b = 2;
    }
}

3. 使用windbg除錯

我準備分三步驟實作,lock前,lock中,lock后,然后拿到這三種情況下的dump檔案來展示 employee 物件的同步塊索引 和 CLR全域同步塊陣列實時情況,

<1> lock前

先把程式跑起來,再從任務管理器中生成dump檔案,

!threads -> ~0s -> !clrstack -l 這三個命令是為了尋找主執行緒堆疊上的區域變數 employee 的記憶體地址,


0:000> !threads
ThreadCount:      2
UnstartedThread:  0
BackgroundThread: 1
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                                                                                        Lock  
       ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1 40b8 00000235222457f0    2a020 Preemptive  0000023523F76D00:0000023523F77FD0 000002352223b0f0 1     MTA 
   6    2 44c8 00000235222705f0    2b220 Preemptive  0000000000000000:0000000000000000 000002352223b0f0 0     MTA (Finalizer) 
0:000>  ~0s
ntdll!ZwReadFile+0x14:
00007ffa`bd7baa64 c3              ret
0:000> !clrstack -l  
OS Thread Id: 0x40b8 (0)
        Child SP               IP Call Site
0000005f721fe748 00007ffabd7baa64 [InlinedCallFrame: 0000005f721fe748] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0000005f721fe748 00007ffaa5d7b7e8 [InlinedCallFrame: 0000005f721fe748] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0000005f721fe710 00007ffaa5d7b7e8 *** ERROR: Module load completed but symbols could not be loaded for mscorlib.ni.dll
DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)

0000005f721fe7f0 00007ffaa65920cc System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef)
    LOCALS:
        <no data>
        <no data>
        <no data>
        <no data>
        <no data>
        <no data>

0000005f721fe880 00007ffaa6591fd5 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)
    LOCALS:
        <no data>
        <no data>

0000005f721fe8e0 00007ffaa5d470f4 System.IO.StreamReader.ReadBuffer()
    LOCALS:
        <no data>
        <no data>

0000005f721fe930 00007ffaa5d47593 System.IO.StreamReader.ReadLine()
    LOCALS:
        <no data>
        <no data>
        <no data>
        <no data>

0000005f721fe990 00007ffaa6738b0d System.IO.TextReader+SyncTextReader.ReadLine()

0000005f721fe9f0 00007ffaa6530d98 System.Console.ReadLine()

0000005f721fea20 00007ffa485d0931 *** WARNING: Unable to verify checksum for ConsoleApp2.exe
ConsoleApp2.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp2\Program.cs @ 19]
    LOCALS:
        0x0000005f721feaa8 = 0x0000023523f72dc0
        0x0000005f721feaa0 = 0x0000000000000000
        0x0000005f721fea98 = 0x0000000000000000

0000005f721fecb8 00007ffaa7af6c93 [GCFrame: 0000005f721fecb8] 

從最后的LOCALS中可以看到,當前主執行緒有三個區域變數,依次是:employee,task1,task2,而其中的 0x0000023523f72dc0 就是employee,

!dumpobj 0x0000023523f72dc0 -> !dumpobj 0000023523f72dd8 找到 employee 在堆上的記憶體區域


0:000>  !dumpobj 0x0000023523f72dc0
Name:        ConsoleApp2.Program+<>c__DisplayClass0_0
MethodTable: 00007ffa484c5af8
EEClass:     00007ffa484c2600
Size:        24(0x18) bytes
File:        C:\dream\Csharp\ConsoleApp1\ConsoleApp2\bin\x64\Debug\ConsoleApp2.exe
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ffa484c5bb8  4000003        8 ConsoleApp2.Employee  0 instance 0000023523f72dd8 employee
0:000> !dumpobj 0000023523f72dd8 
Name:        ConsoleApp2.Employee
MethodTable: 00007ffa484c5bb8
EEClass:     00007ffa484c2678
Size:        24(0x18) bytes
File:        C:\dream\Csharp\ConsoleApp1\ConsoleApp2\bin\x64\Debug\ConsoleApp2.exe
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ffaa57685a0  4000001        8         System.Int32  1 instance                1 a
00007ffaa57685a0  4000002        c         System.Int32  1 instance                2 b


使用選單 view -> memory 查看 0000023523f72dd8 在堆上的布局,從圖上看找的沒有錯哈,


00000235`23f72dc8 d8 2d f7 23 35 02 00 00 00 00 00 00 00 00 00 00  .-.#5...........
00000235`23f72dd8 b8 5b 4c 48 fa 7f 00 00 01 00 00 00 02 00 00 00  .[LH............

從上面看到,00000235`23f72dd8行的前8個位元組就是employee的同步塊索引,此時全部是0,好的,記錄一下這個狀態,

<2> lock中

繼續在控制臺按Enter,從圖中可以看到lock1獲取到了鎖,

使用view -> memory 查看 0000023523f72dd8 記憶體索引地址,可以看到由原來的全0變成了 0000000007000008,如下圖,

然后用 !syncblk -all 把CLR的全域同步塊陣列調出來,看看是不是占了一個坑位,


0:006> !syncblk -all
Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
    1 00000235222af108            0         0 0000000000000000     none    0000023523f77150 System.__ComObject
    2 00000235222af158            0         0 0000000000000000     none    0000023523f77170 System.EventHandler`1[[Windows.Foundation.Diagnostics.TracingStatusChangedEventArgs, mscorlib]]
    3 00000235222af1a8            0         0 0000000000000000     none    0000023523f771b0 Windows.Foundation.Diagnostics.TracingStatusChangedEventArgs
    4 00000235222af1f8            0         0 0000000000000000     none    0000023523f79458 Microsoft.Win32.UnsafeNativeMethods+ManifestEtw+EtwEnableCallback
    5 00000235222af248            0         0 0000000000000000     none    0000023523f7a158 Microsoft.Win32.UnsafeNativeMethods+ManifestEtw+EtwEnableCallback
    6 00000235222af298            0         0 0000000000000000     none    0000023523f7a2f8 System.Object
    7 00000235222af2e8            3         1 00000235222cb320 56a8   6   0000023523f72dd8 ConsoleApp2.Employee
-----------------------------
Total           7
CCW             1
RCW             2
ComClassFactory 0
Free            0


看到最后一行了沒? ConsoleApp2.Employee 占用的坑位編號是7,說明 0000000007000008 和這個 7 做了關聯,同時MonitorHeld=3也說明當前有一個持有執行緒(+1),有一個等待執行緒(+2),所以這個觀點也得到了驗證,

<3> lock后

繼續在控制臺Enter,從圖中可以看到兩個lock都已經結束了,看此時employee會怎樣?

然后還是一樣查看 0000023523f72dd8 的記憶體布局情況,

不過奇怪的是物件的同步塊索引并沒有變,繼續查看同步塊陣列,


0:000> !syncblk -all
Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
    1 00000235222af108            0         0 0000000000000000     none    0000023523f77150 System.__ComObject
    2 00000235222af158            0         0 0000000000000000     none    0000023523f77170 System.EventHandler`1[[Windows.Foundation.Diagnostics.TracingStatusChangedEventArgs, mscorlib]]
    3 00000235222af1a8            0         0 0000000000000000     none    0000023523f771b0 Windows.Foundation.Diagnostics.TracingStatusChangedEventArgs
    4 00000235222af1f8            0         0 0000000000000000     none    0000023523f79458 Microsoft.Win32.UnsafeNativeMethods+ManifestEtw+EtwEnableCallback
    5 00000235222af248            0         0 0000000000000000     none    0000023523f7a158 Microsoft.Win32.UnsafeNativeMethods+ManifestEtw+EtwEnableCallback
    6 00000235222af298            0         0 0000000000000000     none    0000023523f7a2f8 System.Object
    7 00000235222af2e8            0         0 0000000000000000     none    0000023523f72dd8 ConsoleApp2.Employee
    8 00000235222af338            0         0 0000000000000000     none    0000023523f76750 System.IO.TextWriter+SyncTextWriter
-----------------------------
Total           8
CCW             1
RCW             2
ComClassFactory 0
Free            0


從各項都是0來看,它已經處于初始化狀態了,MonitorHeld=0也表示當前無執行緒持有ConsoleApp2.Employee,關于物件同步塊索引沒有變以及陣列中的坑位,可能會被CLR后期惰性洗掉和初始化吧,誰知道呢?

總結

貌似跟蹤下來和CLR via C#說的不是那么一致,如果我是對的,那就是重大發現,如果是錯的,那就是水平有限??????,開個玩笑,可能新版本在底層做了進一步優化吧,

好了,本篇就說到這里,希望對你有幫助


如您有更多問題與我互動,掃描下方進來吧~


轉載請註明出處,本文鏈接:https://www.uj5u.com/net/50938.html

標籤:C#

上一篇:高二女生,該學習C#還是C++,用哪種IDE?

下一篇:求助

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more