ล็อคทำงานอย่างไร


527

ฉันเห็นว่าสำหรับการใช้วัตถุที่ไม่ปลอดภัยเธรดเราห่อรหัสด้วยล็อคเช่นนี้

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}

ดังนั้นจะเกิดอะไรขึ้นเมื่อหลายเธรดเข้าถึงรหัสเดียวกัน (สมมติว่ามันกำลังทำงานอยู่ในเว็บแอปพลิเคชัน ASP.NET) พวกเขาเข้าคิวหรือไม่ ถ้าเป็นเช่นนั้นพวกเขาจะรอนานแค่ไหน?

ประสิทธิภาพการทำงานมีผลกระทบอะไรบ้างจากการใช้ระบบล็อค?


1
^ ลิงค์ตายดู: jonskeet.uk/csharp/threads/index.html
Ivan Pavičić

คำตอบ:


448

lockคำสั่งแปลโดย C # 3.0 ต่อไปนี้:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

ใน C # 4.0 มีการเปลี่ยนแปลงและตอนนี้มันถูกสร้างขึ้นดังนี้:

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    // body
}
finally
{
    if (lockWasTaken)
    {
        Monitor.Exit(temp); 
    }
}

คุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่Monitor.Enterไม่ที่นี่ วิธีอ้าง MSDN:

ใช้Enterเพื่อรับการตรวจสอบบนวัตถุที่ส่งผ่านเป็นพารามิเตอร์ หากเธรดอื่นดำเนินการEnter กับวัตถุ แต่ยังไม่ได้ดำเนินการที่เกี่ยวข้องExitเธรดปัจจุบันจะบล็อกจนกว่าเธรดอื่นจะเผยแพร่วัตถุ เป็นเรื่องถูกกฎหมายสำหรับเธรดเดียวกันให้เรียกใช้ Enterมากกว่าหนึ่งครั้งโดยไม่มีการบล็อก อย่างไรก็ตามExitต้องเรียกจำนวนเท่ากัน ก่อนที่เธรดอื่น ๆ ที่รออยู่บนวัตถุจะปลดบล็อก

Monitor.Enterวิธีการจะรออนันต์; มันจะไม่หมดเวลา


15
ตาม MSDN "การใช้คีย์เวิร์ด lock (C #) หรือ SyncLock (Visual Basic) เป็นที่ต้องการโดยทั่วไปมากกว่าการใช้คลาส Monitor โดยตรงทั้งสองเพราะล็อคหรือ SyncLock มีความรัดกุมมากกว่าและเพราะล็อคหรือ SyncLock ทำให้มั่นใจว่าจอแสดงผลพื้นฐานจะออกแม้ หากรหัสที่ได้รับการป้องกันเกิดข้อผิดพลาดนี่จะสำเร็จได้ด้วยคำสำคัญสุดท้ายซึ่งจะเรียกใช้งานการบล็อกรหัสที่เกี่ยวข้องโดยไม่คำนึงว่ามีข้อผิดพลาดเกิดขึ้นหรือไม่ " msdn.microsoft.com/en-us/library/ms173179.aspx
Aiden Strydom

10
อะไรคือจุดของ var temp = obj; ไลน์. เนื่องจากมันเป็นเพียงการอ้างอิงที่จะเริ่มต้นด้วยสิ่งที่ดีที่ทำให้คนอื่นทำอะไรได้บ้าง
priehl

11
@priehl จะช่วยให้ผู้ใช้สามารถเปลี่ยนแปลงได้objโดยไม่ต้องหยุดระบบทั้งหมด
สตีเวน

7
@Joymon ในที่สุดฟีเจอร์ทุกภาษาคือน้ำตาลซินแทคติค คุณสมบัติทางภาษานั้นเกี่ยวกับการทำให้นักพัฒนามีประสิทธิภาพมากขึ้นและทำให้แอพพลิเคชั่นสามารถบำรุงรักษาได้มากขึ้นเช่นเดียวกับคุณสมบัติล็อคเช่นกัน
สตีเวน

2
แก้ไข. นี่คือจุดประสงค์ทั้งหมดของ - สถานะlockและการตรวจสอบ: เพื่อให้คุณสามารถดำเนินการในหนึ่งเธรดโดยไม่ต้องกังวลเกี่ยวกับเธรดอื่นที่ทำให้เกิดปัญหา
Dizzy H. Muffin

285

มันง่ายกว่าที่คุณคิด

ตามMicrosoft : lockคำหลักช่วยให้มั่นใจว่าเธรดหนึ่งไม่ได้ป้อนส่วนที่สำคัญของรหัสในขณะที่เธรดอื่นอยู่ในส่วนที่สำคัญ หากเธรดอื่นพยายามป้อนรหัสที่ถูกล็อคมันจะรอบล็อกจนกว่าวัตถุจะถูกปล่อยออกมา

lockสายคำหลักEnterที่จุดเริ่มต้นของบล็อกและExitในตอนท้ายของบล็อก lockคำหลักจริง ๆ แล้วจัดการMonitorคลาสที่ส่วนท้าย

ตัวอย่างเช่น:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}

objในโค้ดข้างต้นเป็นครั้งแรกด้ายเข้าสู่ส่วนที่สำคัญและจากนั้นมันจะล็อค เมื่อเธรดอื่นพยายามเข้าใช้เธรดอื่นจะพยายามล็อกobjซึ่งถูกล็อกโดยเธรดแรกแล้ว เธรดที่สองจะต้องรอให้เธรดแรกถูกปล่อยออกobjมา เมื่อเธรดแรกobjจบแล้วเธรดอื่นจะล็อกและจะเข้าสู่ส่วนที่สำคัญ


9
เราควรสร้างวัตถุจำลองเพื่อล็อคหรือเราสามารถล็อคตัวแปรที่มีอยู่ในบริบทได้หรือไม่
batmaci

9
@batmaci - การล็อกวัตถุจำลองที่แยกต่างหากช่วยให้คุณรับประกันได้ว่าไม่มีใครล็อควัตถุนั้นอยู่ หากคุณล็อคข้อมูลและข้อมูลชิ้นเดียวกันนั้นปรากฏให้เห็นภายนอกคุณจะสูญเสียการรับประกันนั้น
Umar Abbas

8
จะเกิดอะไรขึ้นถ้ามีมากกว่าหนึ่งกระบวนการกำลังรอให้การล็อคปล่อย กระบวนการที่รออยู่จะเข้าคิวเพื่อให้พวกเขาจะล็อคส่วนสำคัญในลำดับ FIFO หรือไม่
jstuardo

@jstuardo - พวกเขาเข้าคิว แต่คำสั่งไม่รับประกันว่าจะเป็น FIFO ลองดูลิงค์นี้: albahari.com/threading/part2.aspx
Umar Abbas

คัดลอกโดยไม่ต้องระบุแหล่งที่มาจากnet-informations.com/faq/qk/lock.htm
Martijn Pieters

47

ไม่พวกเขาไม่ได้เข้าคิวพวกเขากำลังนอนหลับ

คำสั่งล็อคของแบบฟอร์ม

lock (x) ... 

โดยที่ x คือนิพจน์ของชนิดข้อมูลอ้างอิงเทียบเท่ากับ

var temp = x;
System.Threading.Monitor.Enter(temp); 
try { ... } 
finally { System.Threading.Monitor.Exit(temp); }

คุณเพียงแค่ต้องรู้ว่าพวกเขากำลังรอซึ่งกันและกันและมีเพียงหนึ่งเธรดเท่านั้นที่จะเข้าสู่ล็อคบล็อกส่วนอื่น ๆ จะรอ ...

จอภาพถูกเขียนอย่างสมบูรณ์ใน. net ดังนั้นจึงเร็วพอและดูที่ Class Monitorพร้อมตัวสะท้อนแสงเพื่อดูรายละเอียดเพิ่มเติม


6
โปรดทราบว่ารหัสที่ปล่อยออกมาสำหรับlockคำสั่งนั้นมีการเปลี่ยนแปลงเล็กน้อยใน C # 4: blogs.msdn.com/b/ericlippert/archive/2009/03/06//
ลูกา

@ArsenMkrt พวกเขาจะไม่ถูกเก็บไว้ในคิว "บล็อก" รัฐ "ฉันคิดว่ามีความแตกต่างบางอย่างระหว่างสถานะ Sleep และ Block ใช่ไหม?
Mohanavel

คุณหมายถึงอะไรที่แตกต่างกัน @Mohanavel
Arsen Mkrtchyan

1
นั่นไม่ใช่คำถาม คำถามเกี่ยวกับคำหลัก "ล็อค" สมมติว่ากระบวนการเข้าสู่ส่วน "ล็อค" นั่นหมายความว่ากระบวนการบล็อกส่วนของรหัสนั้นและไม่มีกระบวนการอื่นใดที่สามารถเข้าสู่ส่วนนั้นได้จนกว่าจะมีการปลดล็อค อืม .... ตอนนี้มีอีก 2 กระบวนการที่พยายามเข้าสู่บล็อกเดียวกัน เนื่องจากมันได้รับการคุ้มครองโดยคำหลัก "ล็อค" พวกเขาจะรอตามที่กล่าวไว้ในฟอรัมนี้ เมื่อกระบวนการแรกปลดล็อค กระบวนการใดที่เข้าสู่บล็อก คนแรกที่พยายามป้อนหรือคนสุดท้าย?
jstuardo

1
ฉันเดาว่าคุณหมายถึงเธรดแทนกระบวนการ ... ถ้าใช่มากกว่าคำตอบคือไม่ไม่มีการรับประกันว่าจะเข้าร่วม ... เพิ่มเติมที่นี่stackoverflow.com/questions/4228864/…
Arsen Mkrtchyan

29

การล็อคจะบล็อกเธรดอื่นจากการเรียกใช้งานรหัสที่มีอยู่ในบล็อกการล็อก เธรดจะต้องรอจนกว่าเธรดภายในบล็อกการล็อคเสร็จสิ้นและการปลดล็อค สิ่งนี้มีผลกระทบด้านลบต่อประสิทธิภาพการทำงานในสภาพแวดล้อมแบบมัลติเธรด หากคุณจำเป็นต้องทำสิ่งนี้คุณควรตรวจสอบให้แน่ใจว่าโค้ดในบล็อกล็อคสามารถดำเนินการได้อย่างรวดเร็ว คุณควรพยายามหลีกเลี่ยงกิจกรรมที่มีราคาแพงเช่นการเข้าถึงฐานข้อมูลเป็นต้น


11

ผลกระทบต่อประสิทธิภาพขึ้นอยู่กับวิธีที่คุณล็อค คุณสามารถค้นหารายการการเพิ่มประสิทธิภาพที่ดีได้ที่นี่: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

โดยทั่วไปคุณควรพยายามล็อคให้น้อยที่สุดเนื่องจากจะทำให้รหัสที่รอของคุณเข้าสู่โหมดสลีป หากคุณมีการคำนวณจำนวนมากหรือรหัสที่ยาวนาน (เช่นการอัปโหลดไฟล์) ในการล็อคจะส่งผลให้ประสิทธิภาพลดลงอย่างมาก


1
แต่การพยายามเขียนรหัสล็อคต่ำมักส่งผลให้เกิดข้อบกพร่องที่ละเอียดและหายากและแก้ไขไม่ได้แม้ว่าคุณจะเป็นผู้เชี่ยวชาญในสาขานี้ การใช้ล็อคมักจะน้อยกว่าความชั่วสองอย่าง คุณควรล็อคให้มากที่สุดเท่าที่คุณต้องการไม่มากไม่น้อย!
LukeH

1
@LukeH: มีรูปแบบการใช้งานบางอย่างที่รหัสล็อคต่ำสามารถทำได้ง่ายและง่าย [ do { oldValue = thing; newValue = updated(oldValue); } while (CompareExchange(ref thing, newValue, oldValue) != oldValue] อันตรายที่ใหญ่ที่สุดคือหากข้อกำหนดนั้นมีวิวัฒนาการเกินกว่าเทคนิคที่สามารถจัดการได้มันอาจเป็นเรื่องยากที่จะปรับใช้รหัสเพื่อจัดการกับสิ่งนั้น
supercat

ลิงก์ใช้งานไม่ได้
CarenRose

8

ส่วนภายในคำสั่งล็อคสามารถดำเนินการได้โดยหนึ่งเธรดดังนั้นกระทู้อื่น ๆ ทั้งหมดจะรอไปเรื่อย ๆ เพื่อให้เธรดที่ถือล็อคให้เสร็จสิ้น สิ่งนี้สามารถทำให้เกิดการหยุดชะงักที่เรียกว่า



โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.