NET กำหนดฟังก์ชันการทำงานแบบมัลติเธรดในเนมสเปซ System.Threading ดังนั้น หากต้องการใช้มัลติเธรด คุณต้องประกาศการอ้างอิงถึงเนมสเปซนี้ก่อน (โดยใช้ System.Threading;)
ก. เริ่มเธรด ตามชื่อที่แนะนำ "เริ่มเธรด" หมายถึงการสร้างและเริ่มเธรด
เธรด thread1 = เธรดใหม่ (ThreadStart ใหม่ (นับ));
โดยที่ Count คือฟังก์ชันที่จะดำเนินการโดยเธรดใหม่
ข. ฆ่าด้าย
"การฆ่าเธรด" คือการกำจัดเธรด เพื่อไม่ให้เสียความพยายาม วิธีที่ดีที่สุดคือตรวจสอบว่าเธรดยังมีชีวิตอยู่หรือไม่ (ผ่านแอตทริบิวต์ IsAlive) ก่อนที่จะฆ่าเธรด จากนั้นจึงเรียกใช้เมธอด Abort เพื่อฆ่าเธรด .
c. การหยุดเธรดชั่วคราวหมายถึงการปล่อยให้เธรดที่กำลังรันอยู่เป็นระยะเวลาหนึ่ง ตัวอย่างเช่น thread.Sleep(1000); คือการปล่อยให้เธรดเข้าสู่โหมดสลีปเป็นเวลา 1 วินาที
d. ลำดับความสำคัญไม่จำเป็นต้องมีคำอธิบาย แอ็ตทริบิวต์ hreadPriority ในคลาส Thread ใช้เพื่อตั้งค่าลำดับความสำคัญ แต่ไม่มีการรับประกันว่าระบบปฏิบัติการจะยอมรับลำดับความสำคัญ ลำดับความสำคัญของเธรดสามารถแบ่งออกเป็น 5 ประเภท: ปกติ, สูงกว่าปกติ, ต่ำกว่าปกติ, สูงสุด และต่ำสุด ตัวอย่างการใช้งานเฉพาะมีดังนี้:
thread.Priority = ThreadPriority.สูงสุด;
e. ระงับเธรด
เมธอด Suspend ของคลาส Thread ใช้เพื่อระงับเธรดจนกว่าจะเรียก Resume ก่อนที่เธรดจะสามารถดำเนินการต่อไปได้ หากเธรดถูกระงับอยู่แล้ว นั่นจะไม่ทำงาน
ถ้า (thread.ThreadState = ThreadState.Running)
-
ด้าย.ระงับ();
-
ฉ. เธรดการกู้คืนใช้เพื่อดำเนินการต่อเธรดที่ถูกระงับเพื่อให้สามารถดำเนินการต่อไปได้ หากเธรดไม่ถูกระงับ ก็จะไม่ทำงาน
ถ้า (thread.ThreadState = ThreadState.Suspended)
-
thread.Resume();
-
ตัวอย่างแสดงไว้ด้านล่างเพื่อแสดงฟังก์ชันการทำงานของเธรดแบบง่าย ตัวอย่างนี้มาจากเอกสารวิธีใช้
ใช้ระบบ;
โดยใช้ System.Threading;
// สถานการณ์เธรดแบบง่าย: เริ่มการทำงานของวิธีการแบบคงที่
// ในเธรดที่สอง
ThreadExample ระดับสาธารณะ {
// เมธอด ThreadProc จะถูกเรียกเมื่อเธรดเริ่มทำงาน
// มันวนซ้ำสิบครั้ง เขียนไปที่คอนโซลแล้วยอม
// เวลาที่เหลือแบ่งส่วนในแต่ละครั้งแล้วสิ้นสุด
โมฆะสาธารณะคงที่ ThreadProc () {
สำหรับ (int i = 0; i <10; i++) {
Console.WriteLine("ThreadProc: {0}", i);
// ให้ผลส่วนเวลาที่เหลือ
เธรด.สลีป(0);
-
-
โมฆะคงที่สาธารณะ Main() {
Console.WriteLine("หัวข้อหลัก: เริ่มหัวข้อที่สอง");
// Constructor สำหรับคลาส Thread ต้องใช้ ThreadStart
// ผู้รับมอบสิทธิ์ที่แสดงถึงวิธีการดำเนินการบน
// thread.C# ทำให้การสร้างผู้รับมอบสิทธิ์นี้ง่ายขึ้น
เธรด t = เธรดใหม่ (ThreadStart ใหม่ (ThreadProc));
// เริ่ม ThreadProc บนตัวประมวลผลเดียว เธรดจะไม่ได้รับ
// เวลาประมวลผลใด ๆ จนกว่าเธรดหลักจะให้ผล
// Thread.Sleep ที่ตามหลัง t.Start() จะเห็นความแตกต่าง
t.เริ่มต้น();
//Thread.Sleep(0);
สำหรับ (int i = 0; i <4; i++) {
Console.WriteLine("หัวข้อหลัก: ทำงานบ้าง");
เธรด.สลีป(0);
-
Console.WriteLine("เธรดหลัก: โทรเข้าร่วม() เพื่อรอจนกว่า ThreadProc สิ้นสุด");
t.เข้าร่วม();
Console.WriteLine("หัวข้อหลัก: ThreadProc.Join กลับมาแล้ว กด Enter เพื่อสิ้นสุดโปรแกรม");
Console.ReadLine();
-
-
รหัสนี้สร้างผลลัพธ์ที่คล้ายกับต่อไปนี้:
หัวข้อหลัก: เริ่มหัวข้อที่สอง
หัวข้อหลัก: ทำงานบางอย่าง
ThreadProc: 0
หัวข้อหลัก: ทำงานบางอย่าง
ThreadProc: 1
หัวข้อหลัก: ทำงานบางอย่าง
ThreadProc:2
หัวข้อหลัก: ทำงานบางอย่าง
ThreadProc: 3
เธรดหลัก: โทรเข้าร่วม () เพื่อรอจนกว่า ThreadProc จะสิ้นสุด
ThreadProc: 4
ThreadProc: 5
ThreadProc: 6
ThreadProc: 7
ThreadProc: 8
ThreadProc: 9
เธรดหลัก: ThreadProc.Join กลับมาแล้ว กด Enter เพื่อสิ้นสุดโปรแกรม
ใน Visul C# เนมสเปซ System.Threading มีคลาสและอินเทอร์เฟซบางอย่างที่เปิดใช้งานการเขียนโปรแกรมแบบมัลติเธรด มีสามวิธีในการสร้างเธรด: Thread, ThreadPool และ Timer ต่อไปนี้เป็นคำแนะนำโดยย่อเกี่ยวกับวิธีการใช้งานทีละรายการ
1. ด้าย
นี่อาจเป็นวิธีที่ซับซ้อนที่สุด แต่ก็มีการควบคุมเธรดที่ยืดหยุ่นที่หลากหลาย ขั้นแรก คุณต้องใช้ Constructor เพื่อสร้างอินสแตนซ์ของเธรด พารามิเตอร์ของมันค่อนข้างง่าย โดยมีผู้รับมอบสิทธิ์ ThreadStart เพียงคนเดียวเท่านั้น: public Thread(ThreadStart start); จากนั้นเรียก Start() เพื่อเริ่มการทำงาน แน่นอนว่า คุณสามารถใช้แอตทริบิวต์ Priority ได้ เพื่อตั้งค่าหรือรับลำดับความสำคัญในการรัน (แจกแจง ThreadPriority: ปกติ, ต่ำสุด, สูงสุด, ต่ำกว่าปกติ, สูงกว่าปกติ)
ตัวอย่างต่อไปนี้จะสร้างอินสแตนซ์เธรดสองตัว t1 และ t2 จากนั้นตั้งค่าลำดับความสำคัญตามลำดับ จากนั้นจึงสตาร์ททั้งสองเธรด (โดยพื้นฐานแล้วทั้งสองเธรดจะเหมือนกัน ยกเว้นว่าเอาต์พุตต่างกัน t1 คือ "1" และ t2 คือ "2 " ตามอัตราส่วนของจำนวนอักขระที่อักขระส่งออก เราสามารถดูอัตราส่วนของเวลา CPU ที่อักขระใช้โดยประมาณได้ ซึ่งยังสะท้อนถึงลำดับความสำคัญตามลำดับด้วย)
โมฆะคงที่หลัก (สตริง [] args)
-
เธรด t1 = เธรดใหม่ (ThreadStart ใหม่ (Thread1));
เธรด t2 = เธรดใหม่ (ThreadStart ใหม่ (Thread2));
t1.Priority = ThreadPriority.BelowNormal;
t2.Priority = ThreadPriority.ต่ำสุด;
t1.เริ่มต้น();
t2.เริ่ม();
-
โมฆะสาธารณะแบบคงที่ Thread1 ()
-
สำหรับ (int i = 1; i < 1,000; i++)
{//เขียน "1" ทุกครั้งที่รันลูป
ที่ ();
Console.Write("1");
-
-
โมฆะสาธารณะคงที่ Thread2 ()
-
สำหรับ (int i = 0; i < 1,000; i++)
{//เขียน "2" ทุกครั้งที่รันลูป
ที่ ();
Console.Write("2");
-
-
โมฆะคงที่สาธารณะ dosth()
{//ใช้เพื่อจำลองการดำเนินการที่ซับซ้อน
สำหรับ (int j = 0; j < 10000000; j++)
-
อินท์เอ=15;
ก = ก*ก*ก*ก;
-
-
ผลลัพธ์ของการรันโปรแกรมข้างต้นคือ:
11111111111111111111111111111111111111111121111111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111111112
จากผลลัพธ์ข้างต้น เราจะเห็นว่าเธรด t1 ใช้เวลา CPU มากกว่า t2 มาก เนื่องจากลำดับความสำคัญของ t1 นั้นสูงกว่าลำดับความสำคัญของ t2 มีดังต่อไปนี้ภาพ:
121211221212121212121212121212121212121212121212121212121212121212121
212121212121212121212121212121212121212121212121212121212121212121212
121212121212121212
จากตัวอย่างข้างต้น เราจะเห็นว่าโครงสร้างของมันคล้ายกับเธรดของผู้ปฏิบัติงาน win32 แต่จะง่ายกว่า คุณเพียงแค่ต้องใช้ฟังก์ชันที่เธรดจะถูกเรียกในฐานะผู้รับมอบสิทธิ์ จากนั้นใช้ผู้รับมอบสิทธิ์เป็นพารามิเตอร์เพื่อ สร้างอินสแตนซ์เธรด เมื่อ Start() ถูกเรียกให้ start ฟังก์ชันที่เกี่ยวข้องจะถูกเรียกใช้ และการดำเนินการจะเริ่มจากบรรทัดแรกของฟังก์ชันนั้น
ต่อไป เราจะรวมคุณสมบัติ ThreadState ของเธรดเพื่อทำความเข้าใจการควบคุมเธรด ThreadState เป็นประเภทการแจงนับที่สะท้อนถึงสถานะของเธรด เมื่ออินสแตนซ์ Thread ถูกสร้างขึ้นครั้งแรก ThreadState ของมันจะ Unstarted เมื่อเธรดเริ่มต้นโดยการเรียก Start() จากนั้น ThreadState จะทำงานอยู่ หลังจากที่เธรดเริ่มทำงานแล้ว หากคุณต้องการให้หยุดชั่วคราว (บล็อก) คุณสามารถเรียก Thread ได้ เมธอด Sleep() มีสองเมธอดโอเวอร์โหลด (Sleep(int), Sleep(Timespan)) ซึ่งเป็นเพียงรูปแบบที่แตกต่างกันสำหรับแสดงระยะเวลา เมื่อฟังก์ชันนี้ถูกเรียกใช้ในเธรด หมายความว่าเธรดจะบล็อก เป็นระยะเวลาหนึ่ง (เวลาถูกกำหนดโดยจำนวนมิลลิวินาทีหรือเวลาที่ส่งผ่านไปยังโหมดสลีป แต่หากพารามิเตอร์เป็น 0 หมายความว่าการระงับเธรดนี้เพื่อให้เธรดอื่นดำเนินการได้ โดยระบุ Infinite เพื่อบล็อกเธรดอย่างไม่มีกำหนด) ที่ คราวนี้ ThreadState ของมันจะกลายเป็น WaitSleepJoin อีกสิ่งหนึ่งที่น่าสังเกตคือฟังก์ชัน Sleep() ถูกกำหนดให้เป็นแบบคงที่? ! นอกจากนี้ยังหมายความว่าไม่สามารถใช้ร่วมกับอินสแตนซ์ของเธรดได้ กล่าวคือ ไม่มีการเรียกที่คล้ายกับ t1.Sleep(10)! เช่นนี้ ฟังก์ชัน Sleep() สามารถเรียกได้โดยเธรดที่ต้องการ "Sleep" เท่านั้น และไม่ได้รับอนุญาตให้เรียกใช้โดยเธรดอื่น เช่นเดียวกับเมื่อใดที่จะ Sleep ก็เป็นเรื่องส่วนตัวที่ผู้อื่นไม่สามารถตัดสินใจได้ . แต่เมื่อเธรดอยู่ในสถานะ WaitSleepJoin และต้องปลุกมันขึ้นมา คุณสามารถใช้เมธอด Thread.Interrupt ซึ่งจะส่ง ThreadInterruptedException บนเธรดก่อน (สังเกตวิธีการเรียกของ Sleep):
โมฆะคงที่หลัก (สตริง [] args)
-
เธรด t1 = เธรดใหม่ (ThreadStart ใหม่ (Thread1));
t1.เริ่มต้น();
t1.ขัดจังหวะ();
E.รอหนึ่ง();
t1.ขัดจังหวะ();
t1.เข้าร่วม();
Console.WriteLine (“t1 สิ้นสุด”);
-
AutoResetEvent แบบคงที่ E = AutoResetEvent ใหม่ (เท็จ);
โมฆะสาธารณะแบบคงที่ Thread1 ()
-
พยายาม
{//ดูได้จากพารามิเตอร์ว่าจะทำให้เกิดการไฮเบอร์เนต
Thread.Sleep (หมดเวลาไม่มีที่สิ้นสุด);
-
จับ (System.Threading.ThreadInterruptedException e)
// ตัวจัดการขัดจังหวะ
Console.WriteLine (" ขัดจังหวะครั้งที่ 1");
-
E.Set();
พยายาม
{// นอน
Thread.Sleep (หมดเวลาไม่มีที่สิ้นสุด);
-
จับ (System.Threading.ThreadInterruptedException e)
-
Console.WriteLine (" ขัดจังหวะครั้งที่ 2");
}//หยุดชั่วคราวเป็นเวลา 10 วินาที
เธรดสลีป (10,000);
-
ผลการทำงานคือ: การขัดจังหวะครั้งที่ 1
ขัดจังหวะครั้งที่ 2
(หลังจาก 10 วินาที) t1 สิ้นสุด
จากตัวอย่างข้างต้น เราจะเห็นว่าเมธอด Thread.Interrupt สามารถปลุกโปรแกรมจากสถานะการบล็อก (WaitSleepJoin) และป้อนตัวจัดการการขัดจังหวะที่เกี่ยวข้อง จากนั้นดำเนินการต่อไป (ThreadState ยังเปลี่ยนเป็น Running) การใช้สิ่งนี้ ฟังก์ชั่นจะต้องสังเกตประเด็นต่อไปนี้:
1. วิธีการนี้ไม่เพียงปลุกการบล็อกที่เกิดจากโหมดสลีปเท่านั้น แต่ยังใช้ได้กับวิธีการทั้งหมดที่สามารถทำให้เธรดเข้าสู่สถานะ WaitSleepJoin (เช่น รอและเข้าร่วม) ดังที่แสดงในตัวอย่างข้างต้น เมื่อใช้งาน คุณควรใส่เมธอดที่ทำให้เกิดการบล็อกเธรดลงในบล็อก try และวางตัวจัดการการขัดจังหวะที่สอดคล้องกันลงในบล็อก catch
2. เรียก Interrupt บนเธรดบางตัว หากอยู่ในสถานะ WaitSleepJoin มันจะเข้าสู่ตัวจัดการการขัดจังหวะที่เกี่ยวข้องสำหรับการดำเนินการ หากไม่อยู่ในสถานะ WaitSleepJoin ในขณะนี้ มันจะถูกขัดจังหวะทันทีเมื่อเข้าสู่สถานะนี้ในภายหลัง . หากมีการเรียก Interrupt หลายครั้งก่อนที่จะขัดจังหวะ เฉพาะการโทรครั้งแรกเท่านั้นที่ถูกต้อง นี่คือสาเหตุที่ฉันใช้การซิงโครไนซ์ในตัวอย่างข้างต้น เพื่อให้แน่ใจว่าการเรียกครั้งที่สองไปยัง Interrupt จะถูกเรียกหลังจากการขัดจังหวะครั้งแรก มิฉะนั้นอาจทำให้เกิดการขัดจังหวะครั้งที่สอง การโทรไม่มีผลใดๆ (หากถูกเรียกก่อนการขัดจังหวะครั้งแรก) คุณสามารถลองลบการซิงโครไนซ์ออกได้ ผลลัพธ์น่าจะเป็น: การขัดจังหวะครั้งที่ 1
ตัวอย่างข้างต้นยังใช้วิธีอื่นอีกสองวิธีเพื่อทำให้เธรดเข้าสู่สถานะ WaitSleepJoin: การใช้วัตถุการซิงโครไนซ์และวิธีการ Thread.Join การใช้วิธีเข้าร่วมนั้นค่อนข้างง่าย หมายความว่าเธรดปัจจุบันที่เรียกใช้เมธอดนี้จะบล็อกจนกว่าเธรดอื่น (t1 ในตัวอย่างนี้) จะยุติลงหรือผ่านเวลาที่กำหนด (หากใช้พารามิเตอร์เวลาด้วย หากมี) เงื่อนไข (ถ้ามี) เกิดขึ้น จะสิ้นสุดสถานะ WaitSleepJoin ทันทีและเข้าสู่สถานะ Running (เงื่อนไขสามารถกำหนดได้จากค่าที่ส่งคืนของเมธอด .Join หากเป็นจริง เธรดจะถูกยกเลิก หากเป็นเท็จ หมดเวลาแล้ว) เธรดยังสามารถหยุดชั่วคราวได้โดยใช้เมธอด Thread.Suspend เมื่อเธรดอยู่ในสถานะกำลังทำงานและมีการเรียกใช้เมธอด Suspend เธรดนั้นจะเข้าสู่สถานะ SuspendRequested แต่จะไม่ถูกระงับทันทีจนกว่าเธรดจะถึงที่ปลอดภัย จุด เธรดค้าง ณ จุดนั้นจะเข้าสู่สถานะถูกระงับ หากถูกเรียกบนเธรดที่ถูกระงับแล้ว มันจะไม่ถูกต้อง หากต้องการดำเนินการต่อ เพียงเรียก Thread.Resume
สิ่งสุดท้ายที่เราพูดถึงคือการทำลายเธรด เราสามารถเรียกเมธอด Abort บนเธรดที่ต้องการทำลายได้ และมันจะส่ง ThreadAbortException บนเธรดนี้ เราสามารถใส่โค้ดบางส่วนลงใน thread ลงใน try block และใส่โค้ดประมวลผลที่เกี่ยวข้องลงใน catch block ที่สอดคล้องกัน เมื่อเธรดกำลังรันโค้ดใน try block ถ้ามีการเรียก Abort มันจะกระโดดเข้าไปใน catch block ที่เกี่ยวข้อง ดำเนินการภายใน catch block มันจะยุติลงหลังจากรันโค้ดใน catch block (หาก ResetAbort ถูกดำเนินการภายใน catch block มันจะแตกต่างออกไป: มันจะยกเลิกคำขอ Abort ปัจจุบันและดำเนินการต่อไปด้านล่าง ดังนั้น หากคุณ ต้องการให้แน่ใจว่าเธรดยุติลง ควรใช้ Join ดังตัวอย่างข้างต้น)
2. เธรดพูล
Thread Pool (ThreadPool) เป็นวิธีการที่ค่อนข้างง่าย เหมาะสำหรับงานสั้นๆ ที่ต้องใช้หลายเธรด (เช่น เธรดบางตัวที่มักถูกบล็อก) ข้อเสียคือ ไม่สามารถควบคุมเธรดที่สร้างขึ้นได้ เนื่องจากแต่ละกระบวนการมีเธรดพูลเพียงอันเดียว และแน่นอนว่าแต่ละโดเมนแอปพลิเคชันมีเธรดพูล (บรรทัด) เพียงอันเดียว คุณจะพบว่าฟังก์ชันสมาชิกของคลาส ThreadPool นั้นคงที่ทั้งหมด! เมื่อคุณเรียก ThreadPool.QueueUserWorkItem, ThreadPool.RegisterWaitForSingleObject ฯลฯ เป็นครั้งแรก อินสแตนซ์ของเธรดพูลจะถูกสร้างขึ้น ต่อไปนี้เป็นข้อมูลเบื้องต้นเกี่ยวกับฟังก์ชันทั้งสองในเธรดพูล:
บูลคงที่สาธารณะ QueueUserWorkItem ( // คืนค่าเป็นจริงหากการโทรสำเร็จ
WaitCallback callBack//ผู้รับมอบสิทธิ์ถูกเรียกโดยเธรดที่จะสร้าง
สถานะของวัตถุ //พารามิเตอร์ที่ส่งผ่านไปยังผู้รับมอบสิทธิ์
)//ฟังก์ชันโอเวอร์โหลดอื่นคล้ายกัน ยกเว้นว่าผู้รับมอบสิทธิ์ไม่มีพารามิเตอร์ ฟังก์ชันของฟังก์ชันนี้คือการจัดคิวเธรดที่จะสร้างลงในเธรดพูล เมื่อจำนวนเธรดที่มีอยู่ในเธรดพูลไม่เป็นศูนย์ ( เธรดพูลได้สร้างเธรด หากมีจำนวนจำกัด (ค่าเริ่มต้นคือ 25) เธรดนี้จะถูกสร้างขึ้น มิฉะนั้น เธรดจะถูกจัดคิวไว้ที่พูลเธรดและรอจนกว่าจะมีเธรดที่พร้อมใช้งาน
สาธารณะคง RegisteredWaitHandle RegisterWaitForSingleObject (
WaitHandle waitObject,//WaitHandle ที่จะลงทะเบียน
WaitOrTimerCallback callBack // ตัวแทนการโทรแบบเธรด
สถานะของวัตถุ//พารามิเตอร์ที่ส่งไปยังผู้รับมอบสิทธิ์
int TimeOut,//หมดเวลา, หน่วยเป็นมิลลิวินาที,
boolexecutOnlyOnce file:// ว่าจะรันเพียงครั้งเดียวหรือไม่
-
ผู้แทนสาธารณะถือเป็นโมฆะ WaitOrTimerCallback (
สถานะของวัตถุ//นั่นคือพารามิเตอร์ที่ส่งไปยังผู้รับมอบสิทธิ์
bool timedOut//true หมายถึง เนื่องจากการเรียกหมดเวลา หรือมิฉะนั้นจะหมายถึง waitObject
-
ฟังก์ชันของฟังก์ชันนี้คือการสร้างเธรดที่รอ เมื่อฟังก์ชันนี้ถูกเรียกใช้ เธรดนี้จะถูกสร้างขึ้น จะอยู่ในสถานะ "ถูกบล็อก" จนกว่าพารามิเตอร์ waitObject จะเปลี่ยนสถานะเป็นสิ้นสุดหรือเมื่อถึงเวลาที่กำหนด TimeOut เป็นที่น่าสังเกตว่า "การบล็อก" นี้แตกต่างอย่างมากจากสถานะ WaitSleepJoin ของ Thread: เมื่อเธรดอยู่ในสถานะ WaitSleepJoin CPU จะปลุกเธรดเป็นประจำเพื่อสำรวจและอัปเดตข้อมูลสถานะ จากนั้นเข้าสู่สถานะ WaitSleepJoin อีกครั้ง การสลับเธรดนั้นใช้ทรัพยากรมาก และเธรดที่สร้างขึ้นด้วยฟังก์ชันนี้จะแตกต่างออกไป CPU จะไม่สลับไปที่เธรดนี้ก่อนที่จะถูกทริกเกอร์ให้ทำงาน มันไม่ต้องใช้เวลาของ CPU และไม่เสียเวลาในการสลับเธรด แต่ CPU ทำอย่างไร รู้ว่าเมื่อไรจะเรียกใช้? ในความเป็นจริง เธรดพูลจะสร้างเธรดเสริมเพื่อตรวจสอบเงื่อนไขทริกเกอร์เหล่านี้ เมื่อตรงตามเงื่อนไข เธรดที่เกี่ยวข้องจะเริ่มต้นขึ้น แน่นอนว่าเธรดเสริมเหล่านี้เองก็ต้องใช้เวลาเช่นกัน แต่ถ้าคุณต้องการสร้างการรอคอยเพิ่มเติม เธรดใช้เธรดพูลข้อดีที่ชัดเจนยิ่งขึ้น ดูตัวอย่างด้านล่าง:
AutoResetEvent แบบคงที่ ev = AutoResetEvent ใหม่ (เท็จ);
int สาธารณะคงหลัก (สตริง [] args)
{ ThreadPool.RegisterWaitForSingleObject(
ทุกๆวัน
ใหม่ WaitOrTimerCallback (WaitThreadFunc)
4,
2000,
false//แสดงว่าตัวจับเวลาจะถูกรีเซ็ตทุกครั้งที่การดำเนินการรอเสร็จสิ้นจนกระทั่งออกจากระบบและรอ
-
ThreadPool.QueueUserWorkItem (WaitCallback ใหม่ (ThreadFunc),8);
เธรดสลีป (10,000);
กลับ 0;
-
โมฆะสาธารณะคง ThreadFunc (วัตถุ b)
{ Console.WriteLine ("วัตถุคือ {0}",b);
สำหรับ(int i=0;i<2;i++)
{ เธรด.สลีป (1,000);
ev.Set();
-
-
โมฆะสาธารณะคง WaitThreadFunc (วัตถุ b, บูล t)
{ Console.WriteLine ("วัตถุคือ {0},t คือ {1}",b,t);
-
ผลลัพธ์ของการดำเนินงานคือ:
วัตถุคือ 8
วัตถุคือ 4, t เป็นเท็จ
วัตถุคือ 4, t เป็นเท็จ
วัตถุคือ 4,t เป็นจริง
วัตถุคือ 4,t เป็นจริง
วัตถุคือ 4,t เป็นจริง
จากผลลัพธ์ข้างต้น เราจะเห็นว่าเธรด ThreadFunc ทำงานหนึ่งครั้ง และ WaitThreadFunc ทำงาน 5 ครั้ง เราสามารถตัดสินเหตุผลในการเริ่มเธรดนี้ได้จากพารามิเตอร์ bool t ใน WaitOrTimerCallback: หาก t เป็นเท็จ แสดงว่า waitObject ไม่เช่นนั้นจะเกิดจากการหมดเวลา นอกจากนี้เรายังสามารถส่งพารามิเตอร์บางตัวไปยังเธรดผ่านอ็อบเจ็กต์ b ได้อีกด้วย
3. ตัวจับเวลา
เหมาะสำหรับวิธีการที่ต้องเรียกเป็นระยะๆ โดยจะไม่ทำงานในเธรดที่สร้างตัวจับเวลา แต่จะทำงานในเธรดแยกต่างหากที่ระบบจัดสรรโดยอัตโนมัติ ซึ่งคล้ายกับวิธี SetTimer ใน Win32 โครงสร้างของมันคือ:
ตัวจับเวลาสาธารณะ(
TimerCallback callback // วิธีการเรียก
สถานะของวัตถุ // พารามิเตอร์ที่ส่งไปยังการโทรกลับ
int DueTime,//ต้องใช้เวลานานแค่ไหนในการเริ่มโทรกลับ?
int period//ช่วงเวลาในการเรียกใช้เมธอดนี้
); // หาก DueTime เป็น 0 การโทรกลับจะดำเนินการโทรครั้งแรกทันที หาก DueTime เป็น Infinite การโทรกลับจะไม่เรียกเมธอดของมัน ตัวจับเวลาถูกปิดใช้งาน แต่สามารถเปิดใช้งานได้อีกครั้งโดยใช้วิธี Change หากระยะเวลาเป็น 0 หรือไม่มีที่สิ้นสุด และ DueTime ไม่ใช่ไม่มีที่สิ้นสุด การเรียกกลับจะเรียกเมธอดของมันหนึ่งครั้ง การทำงานเป็นระยะๆ ของตัวจับเวลาถูกปิดใช้งาน แต่สามารถเปิดใช้งานได้อีกครั้งโดยใช้วิธี Change หากระยะเวลาเป็นศูนย์ (0) หรือไม่มีที่สิ้นสุด และ DueTime ไม่ใช่ไม่มีที่สิ้นสุด การเรียกกลับจะเรียกเมธอดของมันหนึ่งครั้ง การทำงานเป็นระยะๆ ของตัวจับเวลาถูกปิดใช้งาน แต่สามารถเปิดใช้งานได้อีกครั้งโดยใช้วิธี Change
หากเราต้องการเปลี่ยนระยะเวลาและ DueTime ของตัวจับเวลาหลังจากสร้างแล้ว เราสามารถเปลี่ยนได้โดยเรียกวิธี Change ของ Timer:
บูลสาธารณะเปลี่ยน(
เวลาครบกำหนด int,
ระยะเวลา int
);//เห็นได้ชัดว่าพารามิเตอร์ทั้งสองมีการเปลี่ยนแปลงสอดคล้องกับพารามิเตอร์สองตัวในตัวจับเวลา
int สาธารณะคงหลัก (สตริง [] args)
-
Console.WriteLine ("ระยะเวลาคือ 1,000");
Timer tm=ตัวจับเวลาใหม่ (TimerCallback ใหม่ (TimerCall),3,1000,1000);
Thread.Sleep (2000);
Console.WriteLine ("ระยะเวลาคือ 500");
tm.เปลี่ยน (0,800);
เธรดสลีป (3000);
กลับ 0;
-
โมฆะสาธารณะคง TimerCall (วัตถุ b)
-
Console.WriteLine ("timercallback; b คือ {0}",b);
-
ผลลัพธ์ของการดำเนินการคือ:
ระยะเวลาคือ 1,000
timercallback;b คือ 3
timercallback;b คือ 3
ระยะเวลาคือ 500
timercallback;b คือ 3
timercallback;b คือ 3
timercallback;b คือ 3
timercallback;b คือ 3
สรุป
จากการแนะนำสั้นๆ ข้างต้น เราจะเห็นโอกาสต่างๆ ที่ใช้ตามลำดับ: Thread เหมาะสำหรับโอกาสที่ต้องการการควบคุมเธรดที่ซับซ้อน ThreadPool เหมาะสำหรับงานสั้นๆ ที่ต้องใช้หลายเธรด (เช่นบางเธรดที่มักถูกบล็อก) ); Timer เหมาะสำหรับวิธีการที่ต้องเรียกเป็นระยะๆ ตราบใดที่เราเข้าใจลักษณะการใช้งาน เราก็สามารถเลือกวิธีที่เหมาะสมได้ดี