ApartmentState สำหรับหุ่น


120

ฉันเพิ่งแก้ไขข้อบกพร่องโดยใช้สิ่งนี้:

_Thread.SetApartmentState(ApartmentState.STA);

ตอนนี้ฉันอยากจะเข้าใจว่ามันหมายถึงอะไรและทำไมมันถึงได้ผล!


1
โพสต์นี้ช่วยคุณได้
Arsen Mkrtchyan

คำตอบ:


236

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

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

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

มีสองประเภทคือ STA (Single Threaded Apartment) และ MTA ระบุไว้ในการเรียก CoInitializeEx () ซึ่งเป็นฟังก์ชันที่ต้องเรียกใช้โดยเธรดใด ๆ ที่ทำอะไรกับ COM CLR ทำการโทรโดยอัตโนมัติทุกครั้งที่เริ่มเธรด สำหรับเธรดเริ่มต้นหลักของโปรแกรมของคุณจะได้รับค่าที่จะส่งผ่านจากแอตทริบิวต์ [STAThread] หรือ [MTAThread] บนเมธอด Main () ของคุณ ค่าเริ่มต้นคือ MTA สำหรับเธรดที่คุณสร้างขึ้นเองจะถูกกำหนดโดยการเรียกใช้ SetApartmentState () ค่าเริ่มต้นคือ MTA เธรด Threadpool เป็น MTA เสมอซึ่งไม่สามารถเปลี่ยนแปลงได้

มีโค้ดมากมายใน Windows ที่ต้องใช้ STA ตัวอย่างเด่นที่คุณใช้เอง ได้แก่ คลิปบอร์ดลาก + วางและกล่องโต้ตอบเชลล์ (เช่น OpenFileDialog) และโค้ดจำนวนมากที่คุณมองไม่เห็นเช่นโปรแกรม UI Automation และขอเกี่ยวเพื่อสังเกตข้อความ รหัสนั้นไม่จำเป็นต้องเป็นเธรดปลอดภัยผู้เขียนจะมีช่วงเวลาที่ยากลำบากในการทำให้ปลอดภัยโดยไม่ทราบว่าจะใช้โปรแกรมใด ดังนั้นเธรด UI ของโปรเจ็กต์ WPF หรือ Windows Forms ต้องเป็น STA เสมอเพื่อรองรับโค้ดดังกล่าวเช่นเดียวกับเธรดใด ๆ ที่สร้างหน้าต่าง

สัญญาที่คุณทำกับ COM ที่กระทู้ของคุณจะ STA แต่ไม่ต้องการให้คุณทำตามสัญญาที่อพาร์ทเม้นเดียวด้าย พวกเขาค่อนข้างแข็งและคุณอาจยุ่งยากในการวินิจฉัยปัญหาเมื่อคุณผิดสัญญา ข้อกำหนดคือคุณจะไม่บล็อกเธรดเป็นระยะเวลาใด ๆ และคุณต้องปั๊มวนข้อความ ข้อกำหนดหลังเป็นไปตามเธรด UI ของ WPF หรือ Winforms แต่คุณจะต้องดูแลด้วยตัวเองหากคุณสร้างเธรด STA ของคุณเอง การวินิจฉัยทั่วไปสำหรับการทำลายสัญญาคือการหยุดชะงัก

มีการสนับสนุนในตัว CLR เล็กน้อยเพื่อรองรับความต้องการเหล่านี้ btw ช่วยให้คุณหมดปัญหา ล็อคคำสั่งและ WaitOne () วิธีปั๊มห่วงข้อความเมื่อบล็อกบนด้าย STA อย่างไรก็ตามสิ่งนี้ดูแลเฉพาะความต้องการที่ไม่เคยบล็อก แต่คุณยังต้องสร้างลูปข้อความของคุณเอง Application.Run () ทั้งใน WPF และ Winforms

ก่อนหน้านี้ฉันได้ให้คำตอบที่มีรายละเอียดเพิ่มเติมเกี่ยวกับความสำคัญของการวนรอบข้อความเพื่อให้ COM มีความสุข คุณจะพบการโพสต์ที่นี่


4
ตอบโจทย์สุด ๆ ! ข้อบกพร่องที่ฉันแก้ไขคือสำหรับเธรดที่ฉันสร้างขึ้นสำหรับตัวสร้างรายงานที่ทำงานเป็นเวลานานซึ่งใช้การควบคุม WPF เพื่อสร้างส่วนต่างๆของรายงานดังนั้นจึงสมเหตุสมผลแม้ว่าฉันจะไม่ทราบว่าเธรดนั้นมีข้อความวนซ้ำอยู่ .
เบญจพล

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