多線程遇上委托以及靜態(tài)

這是一段很有意思的代碼:

 public class HomeController : Controller
    {
        public delegate string TestDelegateNoResult();

        public ActionResult Index()
        {
            TestDelegateNoResult testDelegateNoResult = new TestDelegateNoResult(new TestLock().Addition);
            //這里實現(xiàn)的是調用部分 嘗試在循環(huán)內以:
            // 1. thread 委托調用 2.thread 實例化調用 3.task 委托調用 4.task 實例化調用
            // 4種情況分別調用Addition方法

            for (int i = 0; i < 10; i++)
            {
                new Thread(() =>
                {
                    System.Diagnostics.Debug.WriteLine(testDelegateNoResult());
                    //System.Diagnostics.Debug.WriteLine(new TestLock().Addition());
                }).Start();
                //Task.Run(() =>
                //{
                //    System.Diagnostics.Debug.WriteLine(testDelegateNoResult());
                //    System.Diagnostics.Debug.WriteLine(new TestLock().Addition());
                //});
            }
            return View();
        }
    }


    public class TestLock
    {
        private object obj = new object();
        private static object static_object = new object();
        private static int staticTotal = 0;
        private int total = 0;
        // 這是一個嘗試用非靜態(tài)鎖 obj,靜態(tài)鎖 static_object
        // 靜態(tài)變量staticTotal,非靜態(tài)變量total 查看累加后數(shù)值及使用線程情況的方法
        public string Addition()
        {
            lock (static_object) // obj
            {
                for (int i = 0; i <= 50; i++)
                {
                    staticTotal += i; // total
                    Thread.Sleep(5);
                }
                return staticTotal.ToString() + " thread:" + Thread.CurrentThread.ManagedThreadId;
            }
        }
    }

來看看我都得到了什么結果呢?

使用Task,委托,lock static object,計算total:1275 thread:4
使用Task,委托,lock static object,計算total:2550 thread:3
使用Task,委托,lock static object,計算total:3825 thread:4
使用Task,委托,lock static object,計算total:5100 thread:3
使用Task,委托,lock static object,計算total:6375 thread:5
使用Task,委托,lock static object,計算total:7650 thread:6
使用Task,委托,lock static object,計算total:8925 thread:7
使用Task,委托,lock static object,計算total:10200 thread:8
使用Task,委托,lock static object,計算total:11475 thread:9
使用Task,委托,lock static object,計算total:12750 thread:10

使用Task,委托,lock static object,計算static total:1275 thread:3
使用Task,委托,lock static object,計算static total:2550 thread:4
使用Task,委托,lock static object,計算static total:3825 thread:3
使用Task,委托,lock static object,計算static total:5100 thread:5
使用Task,委托,lock static object,計算static total:6375 thread:6
使用Task,委托,lock static object,計算static total:7650 thread:7
使用Task,委托,lock static object,計算static total:8925 thread:8
使用Task,委托,lock static object,計算static total:10200 thread:9
使用Task,委托,lock static object,計算static total:11475 thread:10
使用Task,委托,lock static object,計算static total:12750 thread:4

使用Task,委托,lock object,計算static total:1275 thread:4
使用Task,委托,lock object,計算static total:2550 thread:3
使用Task,委托,lock object,計算static total:3825 thread:4
使用Task,委托,lock object,計算static total:5100 thread:5
使用Task,委托,lock object,計算static total:6375 thread:6
使用Task,委托,lock object,計算static total:7650 thread:7
使用Task,委托,lock object,計算static total:8925 thread:8
使用Task,委托,lock object,計算static total:10200 thread:9
使用Task,委托,lock object,計算static total:11475 thread:10
使用Task,委托,lock object,計算static total:12750 thread:3

使用Task,委托,lock object,計算total:1275 thread:3
使用Task,委托,lock object,計算total:2550 thread:4
使用Task,委托,lock object,計算total:3825 thread:3
使用Task,委托,lock object,計算total:5100 thread:4
使用Task,委托,lock object,計算total:6375 thread:5
使用Task,委托,lock object,計算total:7650 thread:7
使用Task,委托,lock object,計算total:8925 thread:6
使用Task,委托,lock object,計算total:10200 thread:8
使用Task,委托,lock object,計算total:11475 thread:9
使用Task,委托,lock object,計算total:12750 thread:10

使用Task,實例化調用,lock static object,計算total:1275 thread:4
使用Task,實例化調用,lock static object,計算total:1275 thread:3
使用Task,實例化調用,lock static object,計算total:1275 thread:4
使用Task,實例化調用,lock static object,計算total:1275 thread:3
使用Task,實例化調用,lock static object,計算total:1275 thread:6
使用Task,實例化調用,lock static object,計算total:1275 thread:7
使用Task,實例化調用,lock static object,計算total:1275 thread:5
使用Task,實例化調用,lock static object,計算total:1275 thread:8
使用Task,實例化調用,lock static object,計算total:1275 thread:9
使用Task,實例化調用,lock static object,計算total:1275 thread:10

使用Task,實例化調用,lock static object,計算static total:1275 thread:3
使用Task,實例化調用,lock static object,計算static total:2550 thread:4
使用Task,實例化調用,lock static object,計算static total:3825 thread:3
使用Task,實例化調用,lock static object,計算static total:5100 thread:5
使用Task,實例化調用,lock static object,計算static total:6375 thread:6
使用Task,實例化調用,lock static object,計算static total:7650 thread:7
使用Task,實例化調用,lock static object,計算static total:8925 thread:8
使用Task,實例化調用,lock static object,計算static total:10200 thread:9
使用Task,實例化調用,lock static object,計算static total:11475 thread:10
使用Task,實例化調用,lock static object,計算static total:12750 thread:4

//這個每次都不一樣,這里列跑兩次的數(shù)據(jù)
//1
使用Task,實例化調用,lock object,計算static total:7617 thread:4
使用Task,實例化調用,lock object,計算static total:7810 thread:3
使用Task,實例化調用,lock object,計算static total:8161 thread:7
使用Task,實例化調用,lock object,計算static total:8214 thread:10
使用Task,實例化調用,lock object,計算static total:8264 thread:8
使用Task,實例化調用,lock object,計算static total:8268 thread:5
使用Task,實例化調用,lock object,計算static total:8268 thread:9
使用Task,實例化調用,lock object,計算static total:8268 thread:6
使用Task,實例化調用,lock object,計算static total:10596 thread:4
使用Task,實例化調用,lock object,計算static total:10596 thread:3
//2
使用Task,實例化調用,lock object,計算static total:8882 thread:4
使用Task,實例化調用,lock object,計算static total:8882 thread:3
使用Task,實例化調用,lock object,計算static total:8882 thread:5
使用Task,實例化調用,lock object,計算static total:8882 thread:7
使用Task,實例化調用,lock object,計算static total:8882 thread:8
使用Task,實例化調用,lock object,計算static total:8882 thread:10
使用Task,實例化調用,lock object,計算static total:8882 thread:9
使用Task,實例化調用,lock object,計算static total:8882 thread:6
使用Task,實例化調用,lock object,計算static total:11350 thread:3
使用Task,實例化調用,lock object,計算static total:11350 thread:4

使用Task,實例化調用,lock object,計算total:1275 thread:6
使用Task,實例化調用,lock object,計算total:1275 thread:4
使用Task,實例化調用,lock object,計算total:1275 thread:8
使用Task,實例化調用,lock object,計算total:1275 thread:10
使用Task,實例化調用,lock object,計算total:1275 thread:9
使用Task,實例化調用,lock object,計算total:1275 thread:5
使用Task,實例化調用,lock object,計算total:1275 thread:3
使用Task,實例化調用,lock object,計算total:1275 thread:7
使用Task,實例化調用,lock object,計算total:1275 thread:6
使用Task,實例化調用,lock object,計算total:1275 thread:4

使用Thread,委托,lock static object,計算total:1275 thread:5
使用Thread,委托,lock static object,計算total:2550 thread:6
使用Thread,委托,lock static object,計算total:3825 thread:8
使用Thread,委托,lock static object,計算total:5100 thread:9
使用Thread,委托,lock static object,計算total:6375 thread:10
使用Thread,委托,lock static object,計算total:7650 thread:11
使用Thread,委托,lock static object,計算total:8925 thread:12
使用Thread,委托,lock static object,計算total:10200 thread:13
使用Thread,委托,lock static object,計算total:11475 thread:14
使用Thread,委托,lock static object,計算total:12750 thread:15

使用Thread,委托,lock static object,計算static total:1275 thread:5
使用Thread,委托,lock static object,計算static total:2550 thread:6
使用Thread,委托,lock static object,計算static total:3825 thread:8
使用Thread,委托,lock static object,計算static total:5100 thread:10
使用Thread,委托,lock static object,計算static total:6375 thread:13
使用Thread,委托,lock static object,計算static total:7650 thread:14
使用Thread,委托,lock static object,計算static total:8925 thread:15
使用Thread,委托,lock static object,計算static total:10200 thread:16
使用Thread,委托,lock static object,計算static total:11475 thread:17
使用Thread,委托,lock static object,計算static total:12750 thread:18

使用Thread,委托,lock object,計算static total:1275 thread:6
使用Thread,委托,lock object,計算static total:2550 thread:7
使用Thread,委托,lock object,計算static total:3825 thread:8
使用Thread,委托,lock object,計算static total:5100 thread:9
使用Thread,委托,lock object,計算static total:6375 thread:10
使用Thread,委托,lock object,計算static total:7650 thread:11
使用Thread,委托,lock object,計算static total:8925 thread:12
使用Thread,委托,lock object,計算static total:10200 thread:13
使用Thread,委托,lock object,計算static total:11475 thread:14
使用Thread,委托,lock object,計算static total:12750 thread:15

使用Thread,委托,lock object,計算total:1275 thread:5
使用Thread,委托,lock object,計算total:2550 thread:6
使用Thread,委托,lock object,計算total:3825 thread:8
使用Thread,委托,lock object,計算total:5100 thread:9
使用Thread,委托,lock object,計算total:6375 thread:10
使用Thread,委托,lock object,計算total:7650 thread:11
使用Thread,委托,lock object,計算total:8925 thread:12
使用Thread,委托,lock object,計算total:10200 thread:13
使用Thread,委托,lock object,計算total:11475 thread:14
使用Thread,委托,lock object,計算total:12750 thread:15


使用Thread,實例化調用,lock static object,計算total:1275 thread:5
使用Thread,實例化調用,lock static object,計算total:1275 thread:6
使用Thread,實例化調用,lock static object,計算total:1275 thread:7
使用Thread,實例化調用,lock static object,計算total:1275 thread:10
使用Thread,實例化調用,lock static object,計算total:1275 thread:14
使用Thread,實例化調用,lock static object,計算total:1275 thread:15
使用Thread,實例化調用,lock static object,計算total:1275 thread:16
使用Thread,實例化調用,lock static object,計算total:1275 thread:17
使用Thread,實例化調用,lock static object,計算total:1275 thread:18
使用Thread,實例化調用,lock static object,計算total:1275 thread:19

使用Thread,實例化調用,lock static object,計算static total:1275 thread:5
使用Thread,實例化調用,lock static object,計算static total:2550 thread:6
使用Thread,實例化調用,lock static object,計算static total:3825 thread:7
使用Thread,實例化調用,lock static object,計算static total:5100 thread:9
使用Thread,實例化調用,lock static object,計算static total:6375 thread:10
使用Thread,實例化調用,lock static object,計算static total:7650 thread:11
使用Thread,實例化調用,lock static object,計算static total:8925 thread:12
使用Thread,實例化調用,lock static object,計算static total:10200 thread:13
使用Thread,實例化調用,lock static object,計算static total:11475 thread:14
使用Thread,實例化調用,lock static object,計算static total:12750 thread:15

//每次結果不一樣
//1
使用Thread,實例化調用,lock object,計算static total:9696 thread:5
使用Thread,實例化調用,lock object,計算static total:10188 thread:10
使用Thread,實例化調用,lock object,計算static total:10338 thread:8
使用Thread,實例化調用,lock object,計算static total:10338 thread:6
使用Thread,實例化調用,lock object,計算static total:10438 thread:13
使用Thread,實例化調用,lock object,計算static total:10438 thread:15
使用Thread,實例化調用,lock object,計算static total:10438 thread:14
使用Thread,實例化調用,lock object,計算static total:10438 thread:12
使用Thread,實例化調用,lock object,計算static total:10438 thread:11
使用Thread,實例化調用,lock object,計算static total:10438 thread:16
//2
使用Thread,實例化調用,lock object,計算static total:9568 thread:5
使用Thread,實例化調用,lock object,計算static total:9470 thread:6
使用Thread,實例化調用,lock object,計算static total:9616 thread:8
使用Thread,實例化調用,lock object,計算static total:10063 thread:10
使用Thread,實例化調用,lock object,計算static total:10063 thread:15
使用Thread,實例化調用,lock object,計算static total:10063 thread:18
使用Thread,實例化調用,lock object,計算static total:10063 thread:17
使用Thread,實例化調用,lock object,計算static total:10063 thread:20
使用Thread,實例化調用,lock object,計算static total:10113 thread:16
使用Thread,實例化調用,lock object,計算static total:10113 thread:19

使用Thread,實例化調用,lock object,計算total:1275 thread:7
使用Thread,實例化調用,lock object,計算total:1275 thread:5
使用Thread,實例化調用,lock object,計算total:1275 thread:11
使用Thread,實例化調用,lock object,計算total:1275 thread:6
使用Thread,實例化調用,lock object,計算total:1275 thread:13
使用Thread,實例化調用,lock object,計算total:1275 thread:12
使用Thread,實例化調用,lock object,計算total:1275 thread:10
使用Thread,實例化調用,lock object,計算total:1275 thread:16
使用Thread,實例化調用,lock object,計算total:1275 thread:15
使用Thread,實例化調用,lock object,計算total:1275 thread:14

好,現(xiàn)在來分析一下以上的結果
1.很顯然發(fā)現(xiàn),使用task會存在線程復用的情況,thread線程一直是往上增的,而且發(fā)現(xiàn)線程用完就退出了,而task線程結束之后會有一段時間的等待(task是基于threadpool),發(fā)現(xiàn)沒有任務需要執(zhí)行然后才退出,這就是task相比thread更強大的線程管理功能:task不會讓線程無限的創(chuàng)建,并且會讓部分任務阻塞等待其他任務執(zhí)行完成。

2.很奇怪的一點:當使用委托方法調用時,無論是不是用的靜態(tài)鎖,是不是用的靜態(tài)變量做數(shù)據(jù)累加,計算出來的total/statictotal都沒有存在相同的情況。我本來理解的是,假如使用的是非靜態(tài)鎖,那么都可能會存在多線程同時修改計數(shù)器的情況,參考實例化直接調用的并使用非靜態(tài)鎖打印出來的數(shù)據(jù),應該是statictotal或total都有出現(xiàn)數(shù)值一樣(說明他們有同時進入房間),但是委托調用的結果出乎了我的意料。


微軟對委托調用的解釋

這就說得通了,由于這個委托是在線程調用之前實例化的,創(chuàng)建線程去調用委托的時候相當于是多個線程調用同一個委托,在調用時依舊是順序調用的,那么就不存在異步搶占資源的情況。
那假如我在線程中再去對委托實例化呢?
讓我們把調用部分代碼改成這樣:

 public ActionResult Index()
        {
            TestDelegateNoResult testDelegateNoResult;
            for (int i = 0; i < 10; i++)
            {
                new Thread(() =>
                {
                    // 在線程中實例化
                    testDelegateNoResult = new TestDelegateNoResult(new TestLock().Addition);
                    System.Diagnostics.Debug.WriteLine(testDelegateNoResult());
                    //System.Diagnostics.Debug.WriteLine(new TestLock().Addition());
                }).Start();
                //Task.Run(() =>
                //{
                //    System.Diagnostics.Debug.WriteLine(testDelegateNoResult());
                //    System.Diagnostics.Debug.WriteLine(new TestLock().Addition());
                //});
            }
            return View();
        }

再來看看得到的結果:

使用Thread,委托調用,lock object,計算static total: 11726 thread:7
使用Thread,委托調用,lock object,計算static total: 11871 thread:9 // 出現(xiàn)重復
使用Thread,委托調用,lock object,計算static total: 11871 thread:10
使用Thread,委托調用,lock object,計算static total: 11921 thread:11
使用Thread,委托調用,lock object,計算static total: 12167 thread:12
使用Thread,委托調用,lock object,計算static total: 12366 thread:16
使用Thread,委托調用,lock object,計算static total: 12216 thread:13
使用Thread,委托調用,lock object,計算static total: 12366 thread:14
使用Thread,委托調用,lock object,計算static total: 12366 thread:15
使用Thread,委托調用,lock object,計算static total: 12019 thread:8

使用Thread,委托調用,lock statc object,計算static total:1275 thread:7
使用Thread,委托調用,lock statc object,計算static total:2550 thread:8
使用Thread,委托調用,lock statc object,計算static total:3825 thread:9
使用Thread,委托調用,lock statc object,計算static total:5100 thread:10
使用Thread,委托調用,lock statc object,計算static total:6375 thread:11
使用Thread,委托調用,lock statc object,計算static total:7650 thread:12
使用Thread,委托調用,lock statc object,計算static total:8925 thread:13
使用Thread,委托調用,lock statc object,計算static total:10200 thread:14
使用Thread,委托調用,lock statc object,計算static total:11475 thread:15
使用Thread,委托調用,lock statc object,計算static total:12750 thread:16

每次實例化的時候相當于創(chuàng)建了一個新的委托,就是多個線程調用多個委托,運行得到的結果就和實例化調用時得到的結果一致了

3.靜態(tài)鎖情況下,分別計算total和statictotal的結果,發(fā)現(xiàn)statictotal是累加的,而total始終為同一個值。
實例化調用的時候,非靜態(tài)的變量每次都會被初始化,而靜態(tài)的變量它會保留之前一次更新的數(shù)值。
因此,statictotal會從上一次的更新開始累加而total每次都從0開始累加


另外,提醒一個關于lock需要注意的地方:
文檔地址:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/lock-statement

lock

lock

我關于這兩句話的理解是,對于可以被公共訪問的實例,大家共享了資源因此lock無效。實例化的時候,lock不同的對象,則鎖無效。
接下來嘗試lock this、lock type、lock string:
1.lock this: 每次被實例化之后 this為不同的對象 因此鎖無效

使用Thread,實例化調用,lock this,計算static total:11199 thread: 12
使用Thread,實例化調用,lock this,計算static total:11536 thread: 13
使用Thread,實例化調用,lock this,計算static total:11877 thread: 14
使用Thread,實例化調用,lock this,計算static total:11927 thread: 15
使用Thread,實例化調用,lock this,計算static total:11877 thread: 16
使用Thread,實例化調用,lock this,計算static total:12220 thread: 19
使用Thread,實例化調用,lock this,計算static total:11877 thread: 17
使用Thread,實例化調用,lock this,計算static total:12122 thread: 18
使用Thread,實例化調用,lock this,計算static total:12320 thread: 20
使用Thread,實例化調用,lock this,計算static total:12320 thread: 21

2.lock type: 每次type都能確認是同一個值,因此鎖是有用的

使用Thread,實例化調用,lock type,計算static total:1275 thread:7
使用Thread,實例化調用,lock type,計算static total:2550 thread:8
使用Thread,實例化調用,lock type,計算static total:3825 thread:9
使用Thread,實例化調用,lock type,計算static total:5100 thread:10
使用Thread,實例化調用,lock type,計算static total:6375 thread:11
使用Thread,實例化調用,lock type,計算static total:7650 thread:12
使用Thread,實例化調用,lock type,計算static total:8925 thread:13
使用Thread,實例化調用,lock type,計算static total:10200 thread:14
使用Thread,實例化調用,lock type,計算static total:11475 thread:15
使用Thread,實例化調用,lock type,計算static total:12750 thread:16

但是!請注意,但是!
我們試試這樣調用:
System.Diagnostics.Debug.WriteLine(new TestLock().BFunction());
System.Diagnostics.Debug.WriteLine(new TestLock().Addition());

用type給兩個方法加鎖

進入BFunction之后,鎖住BFunction同時,也將Addition鎖死了

BFunction sleep over
BFunction sleep over
BFunction sleep over
1275 thread:7
2550 thread:8
3825 thread:9
5100 thread:10
6375 thread:11
7650 thread:12
8925 thread:13
10200 thread:14
11475 thread:15
12750 thread:16

3.lock string:每次訪問的時候string都重置了,因此能確認是同一個值,因此鎖是有用的

使用Thread,實例化調用,lock string,計算static total:1275 thread:7
使用Thread,實例化調用,lock string,計算static total:2550 thread:8
使用Thread,實例化調用,lock string,計算static total:3825 thread:9
使用Thread,實例化調用,lock string,計算static total:5100 thread:10
使用Thread,實例化調用,lock string,計算static total:6375 thread:11
使用Thread,實例化調用,lock string,計算static total:7650 thread:12
使用Thread,實例化調用,lock string,計算static total:8925 thread:13
使用Thread,實例化調用,lock string,計算static total:10200 thread:14
使用Thread,實例化調用,lock string,計算static total:11475 thread:15
使用Thread,實例化調用,lock string,計算static total:12750 thread:16

但是!但是!lock string和lock type一樣都可能存在多個地方使用同樣的鎖的情況
會造成資源鎖死。

總結一下:
1.其實大家不要去看lock中鎖定的是什么,只要關心多線程鎖定的對象是不是為同一個對象,假如鎖定的對象確認是同一個對象,則鎖是有效的,否則鎖即為無效(lock(this)或者lock非靜態(tài)的實例)。
2.鎖必須注意作用域,必須防止在其他地方是否可能使用了相同的鎖,以至于把別人的資源鎖死的情況(lock (type/string))。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容