การใช้ ThreadPool.QueueUserWorkItem ใน ASP.NET ในสถานการณ์ที่มีปริมาณการใช้งานสูง


111

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

นี่คือวิธีที่ฉันทำงานแบบอะซิงโครนัสขนาดเล็กจนถึงตอนนี้:

ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))

และบทความนี้แนะนำให้สร้างเธรดอย่างชัดเจนแทนโดยคล้ายกับ:

new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()

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

คำถามของฉันคือคำแนะนำในบทความถูกต้องหรือไม่?

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

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


พล็อตหนาขึ้น - ฉันพบบทความนี้ ( blogs.msdn.com/nicd/archive/2007/04/16/… ) ซึ่งฉันไม่สามารถถอดรหัสได้ ในแง่หนึ่งดูเหมือนว่า IIS 6.0+ จะประมวลผลคำขอบนเธรดของผู้ปฏิบัติงานพูลเธรดเสมอ (และเวอร์ชันก่อนหน้านี้อาจทำได้) แต่จะมีสิ่งนี้: "อย่างไรก็ตามหากคุณใช้เพจ async ใหม่. NET 2.0 ( Async = "true") หรือ ThreadPool.QueueUserWorkItem () จากนั้นส่วนอะซิงโครนัสของการประมวลผลจะดำเนินการภายใน [เธรดพอร์ตที่สมบูรณ์] " ส่วนอะซิงโครนัสของการประมวลผล ?
Jeff Sternal

อีกอย่างหนึ่ง - สิ่งนี้น่าจะง่ายพอที่จะทดสอบการติดตั้ง IIS 6.0+ (ซึ่งตอนนี้ฉันยังไม่มี) โดยการตรวจสอบว่าเธรดผู้ปฏิบัติงานที่มีอยู่ของเธรดพูลนั้นต่ำกว่าเธรดผู้ปฏิบัติงานสูงสุดหรือไม่จากนั้นทำเช่นเดียวกันภายในคิว รายการงาน
Jeff Sternal

คำตอบ:


104

คำตอบอื่น ๆ ที่นี่ดูเหมือนจะไม่ตรงประเด็นที่สำคัญที่สุด:

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

ซึ่งใช้ได้กับทั้งเธรดฟรีที่สร้างโดยnew Thread(...)และเธรดผู้ปฏิบัติงานในเธรดThreadPoolที่ตอบสนองต่อQueueUserWorkItemคำร้องขอ

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

แต่ถ้าคุณเข้าคิวรายการงานมากพอที่จะทำให้เกิดความอดอยากนี้คุณก็ควรจะอดด้าย! หากคุณใช้งานการดำเนินการที่ต้องใช้ CPU เป็นจำนวนมากในเวลาเดียวกันการมีเธรดของผู้ปฏิบัติงานอื่นเพื่อตอบสนองคำขอ ASP.NET เมื่อเครื่องทำงานหนักเกินไปแล้ว หากคุณกำลังเผชิญกับสถานการณ์นี้คุณต้องออกแบบใหม่ทั้งหมด!

เวลาส่วนใหญ่ที่ฉันเห็นหรือได้ยินเกี่ยวกับโค้ดมัลติเธรดที่ถูกใช้อย่างไม่เหมาะสมใน ASP.NET ไม่ใช่สำหรับการจัดคิวงานที่ต้องใช้ CPU มาก สำหรับการจัดคิวงานที่ผูกกับ I / O และถ้าคุณต้องการทำงาน I / O คุณควรใช้เธรด I / O (I / O Completion Port)

โดยเฉพาะอย่างยิ่งคุณควรใช้การเรียกกลับแบบ async ที่รองรับโดยคลาสไลบรารีที่คุณใช้อยู่ วิธีการเหล่านี้จะมีป้ายกำกับชัดเจนเสมอ พวกเขาเริ่มต้นด้วยคำพูดและBegin Endในขณะที่Stream.BeginRead, Socket.BeginConnect, WebRequest.BeginGetResponseและอื่น ๆ

วิธีการเหล่านี้ทำใช้ThreadPoolแต่พวกเขาใช้ IOCPs ซึ่งจะไม่ยุ่งเกี่ยวกับการร้องขอ ASP.NET เป็นด้ายน้ำหนักเบาชนิดพิเศษที่สามารถ "ตื่น" ได้ด้วยสัญญาณขัดจังหวะจากระบบ I / O และในแอปพลิเคชัน ASP.NET โดยปกติคุณจะมีเธรด I / O หนึ่งเธรดสำหรับเธรดของผู้ปฏิบัติงานแต่ละรายการดังนั้นทุกคำขอเดียวจึงสามารถจัดคิวการดำเนินการ async ได้หนึ่งรายการ นั่นคือการดำเนินการ async หลายร้อยครั้งโดยไม่มีการลดประสิทธิภาพลงอย่างมีนัยสำคัญ (สมมติว่าระบบย่อย I / O สามารถรักษาได้) เป็นมากกว่าที่คุณต้องการ

เพียงแค่เก็บไว้ในใจว่า async ผู้ได้รับมอบหมายไม่ได้ทำงานด้วยวิธีนี้ - ThreadPool.QueueUserWorkItemพวกเขาจะสิ้นสุดการใช้ด้ายคนเช่นเดียวกับ เป็นเพียงวิธีการ async ในตัวของคลาสไลบรารี. NET Framework ที่สามารถทำสิ่งนี้ได้ คุณสามารถทำได้ด้วยตัวเอง แต่มันซับซ้อนและค่อนข้างอันตรายและอาจเกินขอบเขตของการสนทนานี้

คำตอบที่ดีที่สุดสำหรับคำถามนี้ในความคิดของฉันคืออย่าใช้อินสแตนซ์ThreadPool หรือพื้นหลังThreadใน ASP.NETASP.NET ไม่เหมือนกับการหมุนเธรดในแอปพลิเคชัน Windows Forms ที่คุณทำเพื่อให้ UI ตอบสนองและไม่สนใจว่าจะมีประสิทธิภาพเพียงใด ใน ASP.NET ความกังวลของคุณคือปริมาณงานและบริบททั้งหมดที่เปิดใช้งานเธรดของผู้ปฏิบัติงานทั้งหมดจะฆ่าปริมาณงานของคุณอย่างแน่นอนไม่ว่าคุณจะใช้ThreadPoolหรือไม่ก็ตาม

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


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

@ ไมเคิล: การเรียกกลับของ Async โดยทั่วไปค่อนข้างง่ายที่จะตัดหากคุณต้องการเพิ่มระดับให้มากขึ้น คุณสามารถสร้างfaçadeรอบ ๆ วิธีการ async และห่อด้วยวิธีการเดียวที่ใช้Action<T>เป็นโทรกลับเช่น หากคุณหมายความว่าการเลือกว่าจะใช้เธรดผู้ปฏิบัติงานหรือเธรด I / O เกิดขึ้นที่ระดับต่ำสุดนั่นเป็นความตั้งใจ ระดับนั้นเท่านั้นที่จะตัดสินใจได้ว่าต้องการ IOCP หรือไม่
Aaronaught

แม้ว่าในฐานะที่เป็นจุดสนใจ แต่ก็มีเพียง. NET เท่านั้นThreadPoolที่ จำกัด คุณด้วยวิธีนี้อาจเป็นเพราะพวกเขาไม่ไว้ใจนักพัฒนาในการทำให้ถูกต้อง Windows Thread Pool ที่ไม่มีการจัดการมี API ที่คล้ายกันมาก แต่ให้คุณเลือกประเภทเธรดได้
Aaronaught

2
พอร์ตเสร็จสิ้น I / O (IOCP) คำอธิบายของ IOCP ไม่ถูกต้องนัก ใน IOCP คุณมีเธรดผู้ปฏิบัติงานจำนวนคงที่ซึ่งผลัดกันทำงานกับงานที่รอดำเนินการทั้งหมด เพื่อไม่ให้สับสนกับเธรดพูลซึ่งสามารถกำหนดขนาดคงที่หรือไดนามิกได้ แต่มีหนึ่งเธรดต่องาน - สเกลอย่างน่ากลัว ต่างจาก ASYNC คุณไม่มีเธรดเดียวต่องาน เธรด IOCP อาจทำงานได้เล็กน้อยในภารกิจที่ 1 จากนั้นเปลี่ยนเป็นงาน 3 งาน 2 จากนั้นกลับไปที่งาน 1 อีกครั้ง สถานะเซสชันงานจะถูกบันทึกและส่งต่อระหว่างเธรด
MickyD

1
สิ่งที่เกี่ยวกับการแทรกฐานข้อมูล? มีคำสั่ง ASYNC SQL (เช่น Execute) หรือไม่? การแทรกฐานข้อมูลเป็นเรื่องเกี่ยวกับการดำเนินการ I / O ที่ช้าที่สุดรอบ ๆ (เนื่องจากการล็อก) และการที่เธรดหลักรอให้แถวแทรกนั้นเป็นเพียงการสิ้นเปลืองของวงจร CPU
Ian Thompson

45

ตาม Thomas Marquadt จากทีม ASP.NET ที่ Microsoft การใช้ ASP.NET ThreadPool (QueueUserWorkItem) นั้นปลอดภัย

จากบทความ :

ถาม) หากแอปพลิเคชัน ASP.NET ของฉันใช้เธรด CLR ThreadPool ฉันจะไม่อด ASP.NET ซึ่งใช้ CLR ThreadPool เพื่อดำเนินการตามคำขอด้วยหรือไม่ ..

A) สรุปได้ว่าอย่ากังวลกับการอด ASP.NET ของเธรดและหากคุณคิดว่ามีปัญหาที่นี่โปรดแจ้งให้เราทราบและเราจะดูแลมัน

ถาม) ฉันควรสร้างเธรดของตัวเอง (เธรดใหม่) หรือไม่ สิ่งนี้จะไม่ดีกว่าสำหรับ ASP.NET เนื่องจากใช้ CLR ThreadPool

A) กรุณาอย่า หรือจะใส่แบบอื่นไม่ !!! ถ้าคุณฉลาดจริงๆ - ฉลาดกว่าฉันมากคุณก็สร้างชุดข้อความของคุณเองได้ ไม่อย่างนั้นอย่าแม้แต่จะคิดถึงมัน นี่คือสาเหตุบางประการที่คุณไม่ควรสร้างเธรดใหม่บ่อยๆ:

  1. มีราคาแพงมากเมื่อเทียบกับ QueueUserWorkItem ... อย่างไรก็ตามถ้าคุณสามารถเขียน ThreadPool ได้ดีกว่า CLR ฉันขอแนะนำให้คุณสมัครงานที่ Microsoft เพราะเรากำลังมองหาคนแบบคุณอย่างแน่นอน!.

4

เว็บไซต์ไม่ควรไปรอบ ๆ หัวข้อการวางไข่

โดยทั่วไปคุณจะย้ายฟังก์ชันนี้ออกไปยังบริการ Windows ที่คุณติดต่อสื่อสารด้วย (ฉันใช้ MSMQ เพื่อพูดคุยกับพวกเขา)

- แก้ไข

ฉันอธิบายการใช้งานที่นี่: การประมวลผลพื้นหลังตามคิวใน ASP.NET MVC Web Application

- แก้ไข

หากต้องการขยายว่าเหตุใดจึงดีกว่าเธรดเพียงอย่างเดียว:

เมื่อใช้ MSMQ คุณสามารถสื่อสารกับเซิร์ฟเวอร์อื่นได้ คุณสามารถเขียนลงในคิวข้ามเครื่องได้ดังนั้นหากคุณพิจารณาแล้วว่างานเบื้องหลังของคุณใช้ทรัพยากรของเซิร์ฟเวอร์หลักมากเกินไปด้วยเหตุผลบางประการคุณสามารถเปลี่ยนได้เล็กน้อย

นอกจากนี้ยังช่วยให้คุณสามารถประมวลผลงานที่คุณพยายามทำ (ส่งอีเมล / อะไรก็ได้)


4
ฉันไม่เห็นด้วยว่าคำพูดแบบครอบคลุมนี้เป็นจริงเสมอ - โดยเฉพาะอย่างยิ่งสำหรับงานที่ไม่สำคัญ การสร้างบริการ Windows เพียงเพื่อวัตถุประสงค์ในการบันทึกแบบอะซิงโครนัสดูเหมือนจะเกินความจำเป็น นอกจากนี้ตัวเลือกนั้นยังไม่พร้อมใช้งานตลอดเวลา (สามารถปรับใช้ MSMQ และ / หรือบริการ Windows ได้)
Michael Hart

แน่นอน แต่เป็นวิธี 'มาตรฐาน' ในการใช้งานอะซิงโครนัสจากเว็บไซต์ (ธีมคิวเทียบกับกระบวนการอื่น ๆ )
เที่ยงผ้าไหม

2
งานอะซิงโครนัสบางงานไม่ได้ถูกสร้างขึ้นเท่ากันซึ่งเป็นสาเหตุที่มีตัวอย่างเพจอะซิงโครนัสใน ASP.NET ถ้าฉันต้องการดึงผลลัพธ์จากบริการเว็บระยะไกลมาแสดงฉันจะไม่ทำเช่นนั้นผ่าน MSMQ ในกรณีนี้ฉันกำลังเขียนบันทึกโดยใช้โพสต์ระยะไกล ไม่เหมาะกับปัญหาในการเขียนบริการ Windows หรือเชื่อมต่อ MSMQ สำหรับสิ่งนั้น (และฉันไม่สามารถทำได้เนื่องจากแอปนี้อยู่ใน Azure)
Michael Hart

1
ลองพิจารณา: คุณกำลังเขียนถึงโฮสต์ระยะไกลหรือไม่? จะเกิดอะไรขึ้นถ้าโฮสต์นั้นล่มหรือไม่สามารถเข้าถึงได้? คุณต้องการลองเขียนอีกครั้งหรือไม่? บางทีคุณอาจจะอาจจะไม่ ด้วยการใช้งานของคุณมันยากที่จะลองอีกครั้ง ด้วยบริการนี้จะกลายเป็นเรื่องเล็กน้อย ฉันขอขอบคุณที่คุณไม่สามารถทำได้และฉันจะให้คนอื่นตอบปัญหาเฉพาะในการสร้างเธรดจากเว็บไซต์ [เช่นถ้าเธรดของคุณไม่ใช่พื้นหลัง ฯลฯ ] แต่ฉันกำลังสรุป 'ที่เหมาะสม' วิธีการทำ ฉันไม่คุ้นเคยกับสีฟ้าแม้ว่าฉันจะใช้ ec2 (คุณสามารถติดตั้งระบบปฏิบัติการได้ดังนั้นทุกอย่างก็ใช้ได้)
เที่ยงผ้าไหม

@silky ขอบคุณสำหรับความคิดเห็น ฉันได้กล่าวว่า "ไม่สำคัญ" เพื่อหลีกเลี่ยงโซลูชันที่มีน้ำหนักมาก (แต่ทนทาน) นี้ ฉันได้ชี้แจงคำถามแล้วเพื่อให้ชัดเจนว่าฉันไม่ได้ขอแนวทางปฏิบัติที่ดีที่สุดเกี่ยวกับรายการงานที่อยู่ในคิว Azure รองรับสถานการณ์ประเภทนี้ (มีที่จัดเก็บคิวเป็นของตัวเอง) แต่การดำเนินการจัดคิวมีราคาแพงเกินไปสำหรับการบันทึกแบบซิงโครนัสดังนั้นฉันจึงต้องการโซลูชันแบบอะซิงโครนัส ในกรณีของฉันฉันตระหนักถึงข้อผิดพลาดของความล้มเหลว แต่ฉันจะไม่เพิ่มโครงสร้างพื้นฐานเพิ่มเติมในกรณีที่ผู้ให้บริการบันทึกข้อมูลรายนี้ล้มเหลว - ฉันมีผู้ให้บริการบันทึกรายอื่นด้วย
Michael Hart

4

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

นอกจากนี้การใช้งานเธรดจะถูกซ่อนไว้ - หากคุณเริ่มวางไข่เธรดของคุณเองคุณต้องจัดการอย่างถูกต้องเช่นกัน ไม่ได้บอกว่าคุณทำไม่ได้ แต่ทำไมต้องสร้างล้อใหม่

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

หากคุณไม่มีปัญหาด้านประสิทธิภาพการเลือกสร้างเธรดใหม่เพื่อลดความขัดแย้งกับคิวการร้องขอ ASP.NET เป็นการเพิ่มประสิทธิภาพก่อนกำหนดแบบคลาสสิก

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


2

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

http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx

ขอบคุณโทมัส


ลิงก์นั้นมีประโยชน์มากขอบคุณสำหรับโทมัส ฉันสนใจที่จะได้ยินว่าคุณคิดอย่างไรกับคำตอบของ @Aaronaught ด้วย
Michael Hart

ฉันเห็นด้วยกับ Aaronaught และพูดเช่นเดียวกันในบล็อกโพสต์ของฉัน ฉันวางไว้แบบนี้ "ในความพยายามที่จะทำให้การตัดสินใจนี้ง่ายขึ้นคุณควรเปลี่ยน [ไปยังเธรดอื่น] ก็ต่อเมื่อคุณจะบล็อกเธรดคำขอ ASP.NET ในขณะที่คุณไม่ทำอะไรเลยนี่เป็นการทำให้เข้าใจผิดมากเกินไป แต่ฉันกำลังพยายาม เพื่อให้การตัดสินใจเป็นเรื่องง่าย " กล่าวอีกนัยหนึ่งคืออย่าทำเพื่อการคำนวณที่ไม่ปิดกั้น แต่ให้ทำเช่นนั้นหากคุณส่งคำขอบริการเว็บแบบ async ไปยังเซิร์ฟเวอร์ระยะไกล ฟัง Aaronaught! :)
Thomas

1

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

การใช้ ThreadPool ใน ASP.NET จะไม่รบกวนเธรดของผู้ปฏิบัติงาน ASP.NET ใช้ ThreadPool ได้ดี

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

การใช้เธรดใหม่สำหรับทุกข้อความนั้นเกินความจำเป็นอย่างแน่นอน

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


1
@ แซมฉันใช้ log4net จริง ๆ และไม่เห็นบันทึกที่เขียนในเธรดแยกต่างหาก - ฉันต้องเปิดใช้ตัวเลือกอะไรบ้าง?
Michael Hart

1

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

มีเธรดพูลหนึ่งตัวต่อหนึ่งกระบวนการ เธรดพูลมีขนาดดีฟอลต์ 250 เธรดผู้ปฏิบัติงานต่อโปรเซสเซอร์ที่มีอยู่และเธรดการเสร็จสิ้น 1,000 I / O จำนวนเธรดในเธรดพูลสามารถเปลี่ยนแปลงได้โดยใช้เมธอด SetMaxThreads แต่ละเธรดใช้ขนาดสแต็กเริ่มต้นและทำงานตามลำดับความสำคัญเริ่มต้น


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

ดังนั้นฉันเดาว่าคำขอ ASP.NET ใช้เธรดการเสร็จสิ้น I / O (ตรงข้ามกับเธรดผู้ปฏิบัติงาน) - ถูกต้องหรือไม่
Michael Hart

จากบทความของ Fritz Onion ฉันเชื่อมโยงในคำตอบของฉัน: "กระบวนทัศน์นี้เปลี่ยน [จาก IIS 5.0 เป็น IIS 6.0] วิธีจัดการคำขอใน ASP.NET แทนที่จะส่งคำขอจาก inetinfo.exe ไปยังกระบวนการผู้ปฏิบัติงาน ASP.NET ให้ http sys จัดคิวแต่ละคำร้องขอในกระบวนการที่เหมาะสมโดยตรงดังนั้นคำร้องขอทั้งหมดจะถูกเซอร์วิสโดยเธรดของผู้ปฏิบัติงานที่ดึงมาจากพูลเธรด CLR และไม่อยู่บนเธรด I / O " (เน้นของฉัน)
Jeff Sternal

อืมฉันยังไม่แน่ใจทั้งหมด ... บทความนั้นมาจากเดือนมิถุนายน 2546 หากคุณอ่านบทความนี้ตั้งแต่เดือนพฤษภาคม 2547 (ยอมรับว่ายังค่อนข้างเก่า) มีข้อความว่า "หน้าทดสอบ Sleep.aspx สามารถใช้เพื่อเก็บ ASP ได้ เธรด I / O .NET ไม่ว่าง "โดยที่ Sleep.aspx ทำให้เธรดการดำเนินการปัจจุบันเข้าสู่โหมดสลีป: msdn.microsoft.com/en-us/library/ms979194.aspx - เมื่อฉันมีโอกาสฉันจะเห็น ถ้าฉันสามารถเขียนโค้ดตัวอย่างนั้นและทดสอบบน IIS 7 และ. NET 3.5
Michael Hart

ใช่ข้อความของย่อหน้านั้นสับสน ต่อไปในส่วนนั้นจะเชื่อมโยงไปยังหัวข้อการสนับสนุน ( support.microsoft.com/default.aspx?scid=kb;EN-US;816829 ) ที่ชี้แจงสิ่งต่าง ๆ : การเรียกใช้การร้องขอบนเธรดการทำให้ I / O เสร็จสมบูรณ์คือ. NET Framework 1.0 ปัญหาที่ได้รับการแก้ไขใน ASP.NET 1.1 มิถุนายน 2003 Hotfix Rollup Package (หลังจากนั้น "คำขอทั้งหมดจะทำงานบนเธรดของผู้ปฏิบัติงาน") ที่สำคัญกว่าตัวอย่างที่แสดงให้เห็นอย่างชัดเจนว่าสระว่ายน้ำด้าย ASP.NET System.Threading.ThreadPoolเป็นสระว่ายน้ำหัวข้อเดียวกันเปิดเผยโดย
Jeff Sternal

1

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

  1. มีการร้องขอสำหรับบางเพจ
  2. Html จะถูกส่งกลับ
  3. Html บอกให้ไคลเอ็นต์สร้างคำขอเพิ่มเติม (js, css, รูปภาพ ฯลฯ .. )
  4. ข้อมูลเพิ่มเติมจะถูกส่งกลับ

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

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

เธรดและกระบวนการ

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

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

ที่มา


3
@ ขอบคุณสำหรับการป้อนข้อมูล แต่ฉันทราบดีว่าเว็บเซิร์ฟเวอร์ทำงานอย่างไรและไม่เกี่ยวข้องกับคำถามจริงๆ - อีกครั้งอย่างที่ฉันพูดในคำถามฉันไม่ได้ขอคำแนะนำเกี่ยวกับสิ่งนี้ในฐานะสถาปัตยกรรม ปัญหา. ฉันขอข้อมูลทางเทคนิคเฉพาะ เนื่องจากเป็น "ข้อกังวลของคนตัดไม้" ที่ควรมีกระบวนการแบบอะซิงโครนัสอยู่แล้ว - คุณคิดว่ากระบวนการแบบอะซิงโครนัสควรเขียนโดยการนำคนตัดไม้มาใช้อย่างไร?
Michael Hart

0

ฉันไม่เห็นด้วยกับบทความอ้างอิง (C # feeds.com) สร้างเธรดใหม่ได้ง่าย แต่อันตราย จำนวนเธรดที่แอ็คทีฟที่เหมาะสมที่สุดในการรันบนคอร์เดียวนั้นต่ำมากอย่างน่าตกใจ - น้อยกว่า 10 เป็นวิธีที่ง่ายเกินไปที่จะทำให้เครื่องเสียเวลาในการสลับเธรดหากเธรดถูกสร้างขึ้นสำหรับงานเล็กน้อย เธรดเป็นทรัพยากรที่ต้องมีการจัดการ สิ่งที่เป็นนามธรรมของ WorkItem มีไว้เพื่อจัดการกับสิ่งนี้

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

ในที่สุดบทความก็มีข้อความที่ค่อนข้างกว้างเกี่ยวกับอันตรายของการใช้ ThreadPool แต่จริงๆแล้วมันต้องการบางอย่างที่เป็นรูปธรรมในการสำรองข้อมูล


0

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

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


0

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

อย่างไรก็ตามการทำงานในพื้นหลังคุณจะต้องใช้สไตล์ TPL ที่แท้จริงด้านล่างในแอปพลิเคชันเว็บ ASP.Net

var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;

ParallelOptions po = new ParallelOptions();
            po.CancellationToken = ts.Token;
            po.MaxDegreeOfParallelism = 6; //limit here

 Task.Factory.StartNew(()=>
                {                        
                  Parallel.ForEach(collectionList, po, (collectionItem) =>
                  {
                     //Code Here PostLog(logEvent);
                  }
                });
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.