มีกรณีใดบ้างที่ควรใช้ออบเจ็กต์ Thread แบบเก่าแทนโครงสร้างที่ใหม่กว่าอย่างใดอย่างหนึ่ง


112

ฉันเห็นผู้คนมากมายในบล็อกโพสต์และที่นี่ใน SO ไม่ว่าจะหลีกเลี่ยงหรือให้คำแนะนำเกี่ยวกับการใช้งานThreadชั้นเรียนใน C # เวอร์ชันล่าสุด (และฉันหมายถึง 4.0+ แน่นอนด้วยการเพิ่มTask& friends) ก่อนหน้านี้มีการถกเถียงกันเกี่ยวกับความจริงที่ว่าการทำงานของเธรดเก่าธรรมดาสามารถถูกแทนที่ได้ในหลาย ๆ กรณีโดยThreadPoolคลาส

นอกจากนี้กลไกพิเศษอื่น ๆ ยังทำให้Threadคลาสที่น่าสนใจน้อยลงเช่นการTimerเปลี่ยนคอมโบThread+ น่าเกลียดSleepในขณะที่สำหรับ GUI ที่เรามีBackgroundWorkerเป็นต้น

ถึงกระนั้นThreadดูเหมือนว่าจะยังคงเป็นแนวคิดที่คุ้นเคยมากสำหรับบางคน (รวมถึงตัวฉันเองด้วย) คนที่เมื่อต้องเผชิญกับงานที่เกี่ยวข้องกับการดำเนินการแบบคู่ขนานบางอย่างให้ข้ามไปใช้Threadคลาสเก่าที่ดีโดยตรง เมื่อเร็ว ๆ นี้ฉันสงสัยว่าถึงเวลาที่จะต้องแก้ไขแนวทางของตัวเองแล้ว

ดังนั้นคำถามของฉันคือมีกรณีใดบ้างที่จำเป็นหรือมีประโยชน์ที่จะใช้Threadวัตถุเก่าธรรมดาแทนโครงสร้างข้างต้นอย่างใดอย่างหนึ่ง


4
ไม่ใช่ nitpick (หรืออาจจะ ... :)) แต่นั่นคือ. NET (เช่นไม่ จำกัด เฉพาะ C #)
MetalMikester

11
64.5kเพื่อให้ห่างไกลตัวแทนเฉลี่ยสำหรับผู้ใช้ที่ตอบคำถามนี้ เป็นการแสดงที่น่าประทับใจมาก
zzzzBov

1
@zzzzBov: ฉันคิดเกี่ยวกับการโพสต์คำตอบของตัวเอง แต่ตอนนี้ฉันทำไม่ได้เพราะกลัวว่าจะทำให้ค่าเฉลี่ยลดลง :)
Brian Gideon

@BrianGideon ฉันไม่เห็นเหตุผลว่าทำไมจึงควรหยุดคุณหรือใครก็ตามจากการตอบคำถามนี้
zzzzBov

@ ไบรอันกิเดียน: กรุณาโพสต์ :)
ทิวดอ

คำตอบ:


107

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

แต่นั่นไม่ใช่คำถามของคุณจริงๆ คำถามของคุณคือ

มีกรณีใดบ้างที่จำเป็นหรือมีประโยชน์ในการใช้อ็อบเจกต์ Thread แบบเก่าแทนโครงสร้างใดโครงสร้างหนึ่งข้างต้น?

แน่ใจ ในกรณีที่โครงสร้างระดับสูงกว่าอย่างใดอย่างหนึ่งไม่ตรงกับความต้องการของคุณ

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


33
คำแนะนำที่ดีทั้งหมด แต่คุณมีตัวอย่างที่อ็อบเจ็กต์เธรดเก่าธรรมดาอาจดีกว่าสำหรับโครงสร้างที่ใหม่กว่าหรือไม่?
Robert Harvey

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

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

15
@RobertHarvey: แน่นอน ตัวอย่างเช่นหากคุณมีสถานการณ์ "ผู้ผลิต / ผู้บริโภค" แบบคลาสสิกโดยที่คุณต้องการให้มีเธรดหนึ่งเธรดสำหรับนำสิ่งต่างๆออกจากคิวงานและอีกเธรดหนึ่งสำหรับการใส่สิ่งต่างๆลงในคิวงาน ทั้งสองห่วงตลอดไป; พวกเขาไม่ได้จับคู่กับงานที่คุณทำไประยะหนึ่งแล้วได้ผลลัพธ์ คุณไม่จำเป็นต้องมีเธรดพูลเนื่องจากมีเธรดเพียงสองเธรด และคุณสามารถใช้การล็อกและสัญญาณได้อย่างชาญฉลาดเพื่อให้แน่ใจว่าคิวจะปลอดภัยโดยไม่ปิดกั้นเธรดอื่นเป็นเวลานาน
Eric Lippert

@ เอริก: สิ่งที่เป็นนามธรรมที่นำเสนอโดยTPL Dataflowไม่ใช่หรือ
Allon Guralnek

52

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

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

ในหลอดเลือดดำที่คล้ายกัน.NETไม่ได้ห้ามการใช้อาร์เรย์แม้ว่าList<T>จะเหมาะกับหลาย ๆ กรณีที่ผู้คนใช้อาร์เรย์ เพียงเพราะคุณอาจยังต้องการสร้างสิ่งที่ไม่ครอบคลุมโดย lib มาตรฐาน


6
เพียงเพราะเกียร์อัตโนมัติทำงานได้ 99% ไม่ได้หมายความว่าจะไม่มีค่าใด ๆ ในระบบเกียร์ธรรมดาแม้จะละเลยความชอบของผู้ขับขี่
Guvante

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

2
คุณสามารถแทนที่คำว่า "เธรด" ด้วย "โค้ดที่ไม่มีการจัดการ" ในย่อหน้าที่สองและมันจะยังคงดังจริง
Conrad Frix

1
@Joey: โอ้ฉันคิดว่าคุณหมายถึงชิ้นส่วนที่ล้าสมัยคุณค่าของการมีการควบคุมที่ละเอียดอ่อนในราคาของการใช้งานแม้ว่าส่วนใหญ่จะไม่ต้องการก็ตาม BTW ในทางเทคนิคแล้วส่วนที่น่าสนใจของเกียร์ธรรมดาก็คือคลัตช์อยู่ดี
Guvante

3
ถ้าพวกคุณอยากจะไปกับอุปมาส่งสัญญาณตามลำดับมากที่สุด (เช่น BMW ของเกียร์ SMG) จะในความเป็นจริงระบบเกียร์ธรรมดากับคอมพิวเตอร์ดำเนินการคลัทช์และเกียร์เลือก ไม่ใช่ระบบเกียร์ของดาวเคราะห์เหมือนระบบอัตโนมัติทั่วไป
Adam Robinson

13

TaskและThreadเป็นนามธรรมที่แตกต่างกัน หากคุณต้องการสร้างแบบจำลองเธรดThreadคลาสยังคงเป็นตัวเลือกที่เหมาะสมที่สุด เช่นหากคุณต้องการโต้ตอบกับเธรดปัจจุบันฉันไม่เห็นประเภทที่ดีกว่าสำหรับสิ่งนี้

อย่างไรก็ตามในขณะที่คุณชี้ให้เห็นว่า. NET ได้เพิ่มสิ่งที่เป็นนามธรรมเฉพาะหลายตัวซึ่งเป็นที่นิยมThreadในหลาย ๆ กรณี


9

Threadระดับไม่ล้าสมัยก็ยังคงเป็นประโยชน์ในสถานการณ์พิเศษ

ที่ทำงานเราเขียน 'ตัวประมวลผลพื้นหลัง' เป็นส่วนหนึ่งของระบบจัดการเนื้อหา: บริการ Windows ที่ตรวจสอบไดเรกทอรีที่อยู่อีเมลและฟีด RSS และทุกครั้งที่มีสิ่งใหม่ ๆ ปรากฏขึ้นให้ดำเนินการกับมันโดยปกติจะนำเข้า ข้อมูล.

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


ฉันไม่รู้ว่าการใช้ Thread โดยตรงที่นี่เป็นวิธีการแก้ปัญหาที่ถูกต้องหรือไม่ แต่ +1 สำหรับการให้ตัวอย่างที่ต้องไปที่ Thread
Oddthinking

6

ตัวเลือกใหม่ทำให้การใช้งานโดยตรงและการจัดการเธรด (ราคาแพง) น้อยลง

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

ซึ่งเป็นวิธีที่มีราคาแพงและค่อนข้างซับซ้อนในการทำสิ่งต่างๆควบคู่กันไป

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


5

เพื่อตอบคำถามที่ว่า " มีกรณีใดบ้างที่จำเป็นหรือมีประโยชน์ในการใช้อ็อบเจกต์ Thread แบบเก่า " ฉันจะบอกว่า Thread เก่าธรรมดามีประโยชน์ (แต่ไม่จำเป็น) เมื่อคุณมีกระบวนการทำงานที่ยาวนานซึ่งคุณจะชนะ ' ไม่เคยโต้ตอบกับเธรดอื่น

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


System.IO.Threading.Threadฉันยอมรับว่าส่วนใหญ่ของสิ่งที่เย็นขนานใหม่ได้รับการออกแบบงานเม็ดเล็กขนาดเล็กทั่วในระยะสั้นและระยะยาวหัวข้อยังคงควรจะดำเนินการกับ
Ben Voigt

4

อาจไม่ใช่คำตอบที่คุณคาดหวัง แต่ฉันใช้ Thread ตลอดเวลาเมื่อเข้ารหัสกับ. NET Micro Framework MF ค่อนข้างถูกตัดทอนและไม่รวม abstractions ระดับที่สูงขึ้นและคลาส Thread นั้นมีความยืดหยุ่นสูงเมื่อคุณต้องการประสิทธิภาพบิตสุดท้ายจาก CPU MHz ที่ต่ำ


นี่เป็นตัวอย่างที่ดีจริงๆ! ขอบคุณ ฉันไม่ได้คิดเกี่ยวกับเฟรมเวิร์กที่ไม่รองรับคุณสมบัติใหม่นี้
ทิวดอ

3

คุณสามารถเปรียบเทียบคลาส Thread กับ ADO.NET ไม่ใช่เครื่องมือที่แนะนำในการทำงานให้เสร็จ แต่ก็ไม่ล้าสมัย เครื่องมืออื่น ๆ สร้างขึ้นเพื่อให้งานง่ายขึ้น

ไม่ผิดที่จะใช้คลาส Thread กับสิ่งอื่น ๆ โดยเฉพาะอย่างยิ่งหากสิ่งเหล่านั้นไม่มีฟังก์ชันที่คุณต้องการ


3

ไม่ล้าสมัยแน่นอน

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

ดังนั้นคำถามของฉันคือมีกรณีใดบ้างที่จำเป็นหรือมีประโยชน์ในการใช้วัตถุ Thread แบบเก่าแทนโครงสร้างใดโครงสร้างหนึ่งข้างต้น

ฉันจะใช้เธรดและล็อคเฉพาะในกรณีที่มีปัญหาด้านประสิทธิภาพที่รุนแรงเป้าหมายประสิทธิภาพสูง


3

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

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

ในกรณีนั้นฉันไม่พบวิธีที่ดีกว่านี้ในการทำงานกับคลาส Thread โดยตรง ดังที่ Eric Lippert กล่าวไว้ส่วนอื่น ๆ เป็นเพียงนามธรรมระดับสูงกว่าและเหมาะสมที่จะทำงานกับชั้นเรียนระดับล่างเมื่อการใช้งานระดับสูงที่มีอยู่ไม่ตรงกับความต้องการของคุณ เช่นเดียวกับที่บางครั้งคุณต้องทำการเรียก Win32 API เมื่อ. NET ไม่ตอบสนองความต้องการที่แท้จริงของคุณจะมีบางกรณีที่คลาส Thread เป็นตัวเลือกที่ดีที่สุดแม้จะมี "ความก้าวหน้า" ล่าสุดก็ตาม

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