สิ่งใดที่ทำให้เธรดปลอดภัย กฎคืออะไร?


156

มีกฎ / แนวทางโดยรวมสำหรับสิ่งที่ทำให้เมธอดปลอดภัยหรือไม่? ฉันเข้าใจว่าอาจมีสถานการณ์แบบครั้งเดียวมากกว่าหนึ่งล้านครั้ง แต่โดยทั่วไปแล้วเกี่ยวกับอะไร มันง่ายหรือไม่

  1. หากวิธีการเข้าถึงตัวแปรท้องถิ่นเท่านั้นมันเป็นหัวข้อที่ปลอดภัย

มันคืออะไร ที่ใช้สำหรับวิธีการแบบคงที่เช่นกัน?

หนึ่งคำตอบที่ได้รับจาก @Cybis คือ:

ไม่สามารถแชร์ตัวแปรโลคัลระหว่างเธรดได้เนื่องจากแต่ละเธรดได้รับสแต็กของตนเอง

เป็นกรณีสำหรับวิธีคงที่เช่นกัน?

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

ดังนั้นฉันคิดว่าคำถามสุดท้ายของฉันคือ: "มีรายการสั้น ๆ ของกฎที่กำหนดวิธีการเธรดที่ปลอดภัยหรือไม่ถ้าใช่พวกเขาคืออะไร?"

แก้ไข
มีการทำคะแนนที่ดีมากมายที่นี่ ฉันคิดว่าคำตอบที่แท้จริงสำหรับคำถามนี้คือ: "ไม่มีกฎง่ายๆเพื่อความปลอดภัยของเธรด" เย็น. ละเอียด. แต่โดยทั่วไปฉันคิดว่าคำตอบที่ยอมรับนั้นให้บทสรุปสั้น ๆ ที่ดี มีข้อยกเว้นอยู่เสมอ ดังนั้นไม่ว่าจะเป็น ฉันสามารถอยู่กับที่


59
คุณจะไม่เข้าถึงตัวแปรที่เข้าถึงได้โดยเธรดอื่น ๆ โดยไม่มีล็อก
Hans Passant

4
Hanth pathant กลายเป็นไอกอร์!
Martin James

3
นอกจากนี้ .. 'คุณจะไม่สามารถเข้าถึงตัวแปรที่เข้าถึงได้โดยเธรดอื่น ๆ ที่ไม่มีการล็อก' - มันไม่สำคัญว่าถ้าค่าที่อ่านนั้นไม่ใช่ค่าล่าสุดหรือไม่ถูกต้องอย่างแท้จริง
Martin James

นี่เป็นบล็อกที่ดีโดย Eric ที่จะนำคุณไปสู่พายุหมุน
RBT

คำตอบ:


139

หากเมธอด (อินสแตนซ์หรือสแตติก) อ้างอิงตัวแปรที่กำหนดขอบเขตภายในเมธอดนั้นเท่านั้นมันจะปลอดภัยต่อเธรดเนื่องจากแต่ละเธรดมีสแต็กของตนเอง:

ในอินสแตนซ์นี้หลายเธรดสามารถเรียกThreadSafeMethodพร้อมกันโดยไม่มีปัญหา

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number; // each thread will have its own variable for number.
        number = parameter1.Length;
        return number;
    }
}

สิ่งนี้ก็เป็นจริงเช่นกันถ้าเมธอดเรียกเมธอด class อื่นซึ่งอ้างอิงเฉพาะตัวแปรที่กำหนดขอบเขต:

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number;
        number = this.GetLength(parameter1);
        return number;
    }

    private int GetLength(string value)
    {
        int length = value.Length;
        return length;
    }
}

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

public class Thing
{
    private string someValue; // all threads will read and write to this same field value

    public int NonThreadSafeMethod(string parameter1)
    {
        this.someValue = parameter1;

        int number;

        // Since access to someValue is not synchronised by the class, a separate thread
        // could have changed its value between this thread setting its value at the start 
        // of the method and this line reading its value.
        number = this.someValue.Length;
        return number;
    }
}

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

เพื่อให้แน่ใจว่าการทำงานพร้อมกันที่เหมาะสมคุณต้องใช้การล็อค

สำหรับข้อมูลเพิ่มเติมโปรดดูที่ล็อคคำสั่ง C # อ้างอิงและReadWriterLockSlim

การล็อคนั้นมีประโยชน์สำหรับการใช้งานฟังก์ชั่นครั้งละหนึ่งอัน
ReadWriterLockSlimมีประโยชน์ถ้าคุณต้องการผู้อ่านหลายคนและนักเขียนคนเดียว


15
ในตัวอย่างที่สามprivate string someValue;ไม่ใช่staticดังนั้นแต่ละอินสแตนซ์จะได้รับสำเนาของตัวแปรนั้นแยกต่างหาก คุณช่วยอธิบายได้ไหมว่ามันไม่ปลอดภัยไหม?
Bharadwaj

29
@Bharadwaj หากมีหนึ่งอินสแตนซ์ของThingคลาสที่เข้าถึงได้โดยหลายเธรด
Trevor Pilley

111

หากวิธีการเข้าถึงตัวแปรท้องถิ่นเท่านั้นมันเป็นหัวข้อที่ปลอดภัย มันคืออะไร

ไม่อย่างแน่นอน คุณสามารถเขียนโปรแกรมด้วยตัวแปรโลคัลเดียวที่เข้าถึงได้จากเธรดเดี่ยวที่ไม่ได้เป็น threadsafe:

https://stackoverflow.com/a/8883117/88656

ที่ใช้สำหรับวิธีการแบบคงที่เช่นกัน?

ไม่ได้อย่างแน่นอน.

หนึ่งคำตอบที่จัดทำโดย @Cybis คือ: "ไม่สามารถแชร์ตัวแปรโลคัลระหว่างเธรดได้เนื่องจากแต่ละเธรดได้รับสแต็กของตนเอง"

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

เป็นกรณีสำหรับวิธีคงที่เช่นกัน?

ไม่ได้อย่างแน่นอน.

หากวิธีการใดผ่านวัตถุอ้างอิงความปลอดภัยของเธรดจะทำลายได้หรือไม่

อาจจะ.

ฉันได้ทำการวิจัยและมีหลายอย่างเกี่ยวกับบางกรณี แต่ฉันหวังว่าจะสามารถกำหนดได้โดยใช้กฎเพียงไม่กี่แนวทางเพื่อปฏิบัติตามเพื่อให้แน่ใจว่าวิธีนั้นปลอดภัยสำหรับเธรด

คุณจะต้องเรียนรู้ที่จะอยู่กับความผิดหวัง เรื่องนี้เป็นเรื่องยากมาก

ดังนั้นฉันคิดว่าคำถามสุดท้ายของฉันคือ: "มีรายการสั้น ๆ ของกฎที่กำหนดวิธีการเธรดที่ปลอดภัยหรือไม่

Nope ในขณะที่คุณเห็นจากตัวอย่างของฉันก่อนหน้านี้เป็นวิธีการที่ว่างเปล่าสามารถที่ไม่ด้ายปลอดภัย คุณอาจถามเช่นกัน "มีรายการย่อของกฎที่รับรองว่าวิธีการนั้นถูกต้องหรือไม่ " ไม่ไม่มี ความปลอดภัยของด้ายไม่มีอะไรมากไปกว่าความถูกต้องที่ซับซ้อนอย่างยิ่ง

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

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


14
คำแถลงหลัก: ความปลอดภัยของเธรดนั้นเป็นสากลไม่ใช่ทรัพย์สินในท้องถิ่นของโปรแกรม
เกร็ก D

5
@BobHorn: class C { public static Func<int> getter; public static Action<int> setter; public static void M() { int x = 0; getter = ()=>x; setter = y=>{x=y;};} } โทร M () จากนั้นโทร C.getter และ C.setter ในสองกระทู้ที่แตกต่างกัน ขณะนี้ตัวแปรโลคัลสามารถเขียนและอ่านจากสองเธรดที่แตกต่างกันแม้ว่าจะเป็นโลคัล อีกครั้ง: การกำหนดลักษณะของตัวแปรท้องถิ่นคือว่ามันเป็นท้องถิ่นไม่ว่าจะเป็นในกองของด้าย
Eric Lippert

3
@ BobHorn: ฉันคิดว่ามันมีอะไรมากกว่าที่จะเข้าใจเกี่ยวกับเครื่องมือของเราในระดับที่เราสามารถพูดเกี่ยวกับพวกเขาด้วยอำนาจและความรู้ ยกตัวอย่างเช่นคำถามดั้งเดิมทำให้ขาดความเข้าใจเกี่ยวกับตัวแปรท้องถิ่น ข้อผิดพลาดนี้ค่อนข้างทั่วไป แต่ความจริงก็คือมันเป็นข้อผิดพลาดและควรได้รับการแก้ไข ความหมายของตัวแปรท้องถิ่นค่อนข้างแม่นยำและควรได้รับการปฏิบัติตาม :) นี่ไม่ใช่คำตอบสำหรับ "คน" ที่ไม่ได้รับกรณีทางพยาธิวิทยาที่มีอยู่ นี่คือคำชี้แจงเพื่อให้คุณสามารถเข้าใจสิ่งที่คุณถามจริง :)
Greg D

7
@EricLippert: เอกสาร MSDN สำหรับหลายคลาสประกาศว่าสมาชิก 'สาธารณะคงที่ (แชร์ใน Visual Basic) ประเภทนี้เป็นเธรดที่ปลอดภัย สมาชิกอินสแตนซ์ใด ๆ ไม่รับประกันว่าจะปลอดภัยสำหรับเธรด ' หรือบางครั้ง 'ประเภทนี้ปลอดภัยสำหรับเธรด' (เช่นสตริง) การรับประกันเหล่านี้จะเกิดขึ้นได้อย่างไรเมื่อความปลอดภัยของเธรดเป็นข้อกังวลระดับโลก
Mike Zboray

3
@mikez: เป็นคำถามที่ดี วิธีการเหล่านั้นไม่ได้ทำให้เกิดการกลายพันธุ์ใด ๆ หรือเมื่อพวกเขาพวกเขากลายพันธุ์ของรัฐได้อย่างปลอดภัยโดยไม่ต้องล็อคหรือถ้าพวกเขาใช้ล็อคแล้วพวกเขาก็ทำเช่นนั้นในทางที่รับประกันได้ว่า สตริงเป็นโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปซึ่งวิธีการไม่ได้กลายพันธุ์อะไรดังนั้นสตริงจึงสามารถทำให้ปลอดภัยได้อย่างง่ายดาย
Eric Lippert

11

ไม่มีกฎที่ยากและรวดเร็ว

ต่อไปนี้คือกฎบางประการที่จะทำให้เธรดโค้ดปลอดภัยใน. NET และทำไมกฎเหล่านี้จึงไม่ดี:

  1. ฟังก์ชั่นและฟังก์ชั่นทั้งหมดที่เรียกจะต้องบริสุทธิ์ (ไม่มีผลข้างเคียง) และใช้ตัวแปรท้องถิ่น แม้ว่าสิ่งนี้จะทำให้โค้ดของคุณปลอดภัย แต่ก็มีสิ่งที่น่าสนใจเล็กน้อยที่คุณสามารถทำได้ด้วยข้อ จำกัด นี้ใน. NET
  2. ฟังก์ชั่นทุกอย่างที่ทำงานบนวัตถุทั่วไปจะต้องlockเป็นสิ่งที่พบบ่อย การล็อคทั้งหมดจะต้องทำในลำดับเดียวกัน สิ่งนี้จะทำให้เธรดโค้ดปลอดภัย แต่จะช้าอย่างไม่น่าเชื่อและคุณอาจไม่ใช้หลายเธรด
  3. ...

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


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

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

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