[C#基礎]說說lock到底鎖誰?(補充與修改)


摘要

今天在園子里面有園友反饋關於[C#基礎]說說lock到底鎖誰?文章中lock(this)的問題。后來針對文章中的例子,仔細想了一下,確實不准確,才有了這篇文章的補充,已經對文章中的demo進行修改。

lock(this)

一個例子

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LockTest
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLock testlock = new TestLock();
            Thread th = new Thread(() =>
            {
                //模擬死鎖:造成死鎖,使lock無法釋放,在i=5時,跳出死循環,釋放lock
                testlock.DoWorkWithLock();
            });
            th.Start();
            Thread.Sleep(1000);
            Thread th2 = new Thread(() =>
            {              
                //這個地方你可能會有疑惑,但存在這種情況,比如你封裝的dll,對其它開發人員不是可見的
                //開發人員很有可能在他的邏輯中,加上一個lock保證方法同時被一個線程調用,但這時有其它的線程正在調用該方法,
                //但並沒有釋放,死鎖了,那么在這里就不會被執行,除非上面的線程釋放了lock鎖定的對象。這里的lock也可以理解為一個標識,線程1被鎖定的對象
//是否已經被釋放,
//如果沒有釋放,則無法繼續訪問lock塊中的代碼。
lock (testlock) { // 如果該對象中lock(this)不釋放(testlock與this指的是同一個對象),則其它線程如果調用該方法,則會出現直到lock(this)釋放后才能繼續調用。 testlock.MotherCallYouDinner(); testlock.DoWorkWithLock(); } }); th2.Start(); Console.Read(); } } class TestLock { public static readonly object objLock = new object(); /// <summary> /// 該方法,希望某人在工作的時候,其它人不要打擾(希望只有一個線程在執行) /// </summary> /// <param name="methodIndex"></param> public void DoWorkWithLock() { //鎖當前對象 lock (this) { Console.WriteLine("lock this"); int i = 0; while (true) { Console.WriteLine("At work, do not disturb...,Thread id is " + Thread.CurrentThread.ManagedThreadId.ToString()); Thread.Sleep(1000); if (i == 5) { break; } Console.WriteLine(i.ToString()); i++; } } Console.WriteLine("lock dispose"); } public void MotherCallYouDinner() { Console.WriteLine("Your mother call you to home for dinner."); } } }

測試

demo說明:main方法中,創建了一個對象testlock對象,線程1執行該對象的DoWorkWithLock方法,因為死鎖(5s后釋放),造成lock(this)無法釋放,則導致了方法MotherCallYouDinner,DoWorkWithLock在線程2中無法被調用,直到lock(this)釋放,lock(testlock)才能繼續執行,可以這么理解,由於鎖定的同一個對象,線程1釋放了鎖定的對象,其它線程才能訪問。

lock(static readonly object)

那么通過lock(static object)方式呢,能不能保證lock塊內的方法,同時只被一個線程執行呢,並且線程2能訪問到MotherCallYouDinner方法。而不像上面出現的那種情況,如果不釋放lock(this),導致線程2都無法執行代碼邏輯。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LockTest
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLock testlock = new TestLock();
            Thread th = new Thread(() =>
            {
                //模擬死鎖:造成死鎖,使lock無法釋放,在i=5時,跳出死循環,釋放lock
                testlock.DoWorkWithLock();
            });
            th.Start();
            Thread.Sleep(1000);
            Thread th2 = new Thread(() =>
            {              
               
                lock (testlock)
                {                
                    testlock.MotherCallYouDinner();
                    testlock.DoWorkWithLock();
                }
            });
            th2.Start();
            Console.Read();
        }
    }

    class TestLock
    {
        public static readonly object objLock = new object();
        /// <summary>
        ///  該方法,希望某人在工作的時候,其它人不要打擾(希望只有一個線程在執行)
        /// </summary>
        /// <param name="methodIndex"></param>
        public void DoWorkWithLock()
        {
            //
            lock (objLock)
            {
                Console.WriteLine("lock this");
                int i = 0;
                while (true)
                {
                    Console.WriteLine("At work, do not disturb...,Thread id is " + Thread.CurrentThread.ManagedThreadId.ToString());
                    Thread.Sleep(1000);
                    if (i == 5)
                    {
                        break;
                    }
                    Console.WriteLine(i.ToString());
                    i++;
                }
            }
            Console.WriteLine("lock dispose");
        }
        public void MotherCallYouDinner()
        {
            Console.WriteLine("Your mother call you to home for dinner.");
        }
    }
}

測試

可以看到,將lock(this)更換為鎖定私有的靜態對象,線程2執行了,首先輸出了“Your mother call you to home for dinner.”,同時實現了DoWorkWithLock方法中lock的代碼塊當前只被一個線程執行,直到lcok(objlock)被釋放。因為鎖定的對象,外部不能訪問,線程2不再關心lock(this)是不是已經釋放,都會執行,當然也保證了方法DoWorkWithLock同時被一個線程訪問。

總結

1、避免使用lock(this),因為無法保證你提供的方法,在外部類中使用的時候,開發人員會不會鎖定當前對象。

通常,應避免鎖定 public 類型,否則實例將超出代碼的控制范圍。常見的結構 lock (this)lock (typeof (MyType)) 和 lock ("myLock") 違反此准則:

  • 如果實例可以被公共訪問,將出現 lock (this) 問題。

  • 如果 MyType 可以被公共訪問,將出現 lock (typeof (MyType)) 問題。

  • 由於進程中使用同一字符串的任何其他代碼將共享同一個鎖,所以出現 lock(“myLock”) 問題。

最佳做法是定義 private 對象來鎖定, 或 private static 對象變量來保護所有實例所共有的數據。

這里只是說明lock(this)的問題,雖然極端。但開發中,不能保證不會發生。

2、最好使用私有的靜態只讀的鎖對象,保證不會影響其他邏輯的正常執行。

3、盡量避免死鎖的發生。

資料,如果仍然不明白可以參考下面的鏈接

Why is lock(this) {…} bad?

非常感謝,@咕-咚 在上篇文章中,對demo提出的疑問,引發進一步的思考。當然,如果還有不妥的地方,歡迎指正,討論。


免責聲明!

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



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