ใครก็ได้โปรดอธิบายข้อความนี้ที่เขียนไว้ในลิงค์นี้
Invoke(Delegate):
ดำเนินการผู้รับมอบสิทธิ์ที่ระบุบนเธรดที่เป็นเจ้าของหมายเลขอ้างอิงหน้าต่างของตัวควบคุม
ใครสามารถอธิบายความหมาย (โดยเฉพาะตัวหนา) ฉันไม่สามารถเข้าใจได้อย่างชัดเจน
ใครก็ได้โปรดอธิบายข้อความนี้ที่เขียนไว้ในลิงค์นี้
Invoke(Delegate):
ดำเนินการผู้รับมอบสิทธิ์ที่ระบุบนเธรดที่เป็นเจ้าของหมายเลขอ้างอิงหน้าต่างของตัวควบคุม
ใครสามารถอธิบายความหมาย (โดยเฉพาะตัวหนา) ฉันไม่สามารถเข้าใจได้อย่างชัดเจน
คำตอบ:
คำตอบสำหรับคำถามนี้อยู่ที่การควบคุม C #
การควบคุมใน Windows Forms ถูกผูกไว้กับเธรดเฉพาะและไม่ใช่เธรดที่ปลอดภัย ดังนั้นหากคุณกำลังเรียกใช้เมธอดของตัวควบคุมจากเธรดอื่นคุณต้องใช้วิธีการเรียกใช้ของตัวควบคุมวิธีใดวิธีหนึ่งเพื่อจัดการการเรียกไปยังเธรดที่เหมาะสม คุณสมบัตินี้สามารถใช้เพื่อพิจารณาว่าคุณต้องเรียกใช้เมธอด invoke หรือไม่ซึ่งจะมีประโยชน์หากคุณไม่ทราบว่าเธรดใดเป็นเจ้าของคอนโทรล
อย่างมีประสิทธิภาพสิ่งที่ Invoke ทำคือให้แน่ใจว่ารหัสที่คุณเรียกใช้เกิดขึ้นบนเธรดที่ตัวควบคุม "ใช้งานอยู่" ได้อย่างมีประสิทธิภาพป้องกันข้อยกเว้นของเธรด
จากมุมมองทางประวัติศาสตร์ใน. Net 1.1 สิ่งนี้ได้รับอนุญาตจริง ความหมายก็คือคุณสามารถลองรันโค้ดบนเธรด "GUI" จากเธรดพื้นหลังใดก็ได้และส่วนใหญ่จะได้ผล บางครั้งมันอาจทำให้แอปของคุณออกจากระบบเนื่องจากคุณขัดจังหวะเธรด GUI อย่างมีประสิทธิภาพในขณะที่กำลังทำอย่างอื่น นี่คือข้อยกเว้นแบบไขว้ - ลองนึกภาพว่าพยายามอัปเดตกล่องข้อความในขณะที่ GUI กำลังวาดภาพอย่างอื่น
อย่างมีประสิทธิภาพคุณกำลังขัดจังหวะคิวซึ่งอาจมีผลกระทบที่คาดไม่ถึงมากมาย วิงวอนเป็นอย่างมีประสิทธิภาพ "สุภาพ" วิธีการได้รับสิ่งที่คุณต้องการจะทำลงในคิวนั้นและกฎนี้ถูกบังคับจากสุทธิ 2.0 เป็นต้นไปผ่านทางโยนInvalidOperationException
เพื่อให้เข้าใจถึงสิ่งที่เกิดขึ้นจริงเบื้องหลังและความหมายของ "GUI Thread" การทำความเข้าใจว่า Message Pump หรือ Message Loop คืออะไร
คำตอบนี้มีคำตอบอยู่แล้วในคำถาม " ปั๊มข้อความคืออะไร " และขอแนะนำให้อ่านเพื่อทำความเข้าใจกลไกจริงที่คุณคาดหวังเมื่อโต้ตอบกับตัวควบคุม
การอ่านอื่น ๆ ที่คุณอาจพบว่ามีประโยชน์ ได้แก่ :
กฎสำคัญประการหนึ่งของการเขียนโปรแกรม Windows GUI คือเฉพาะเธรดที่สร้างตัวควบคุมเท่านั้นที่สามารถเข้าถึงและ / หรือแก้ไขเนื้อหาได้ (ยกเว้นข้อยกเว้นที่มีการจัดทำเป็นเอกสารบางส่วน) ลองทำจากเธรดอื่น ๆ และคุณจะได้รับพฤติกรรมที่คาดเดาไม่ได้ตั้งแต่การหยุดชะงักไปจนถึงข้อยกเว้นไปจนถึง UI ที่อัปเดตครึ่งหนึ่ง วิธีที่ถูกต้องในการอัปเดตตัวควบคุมจากเธรดอื่นคือการโพสต์ข้อความที่เหมาะสมไปยังคิวข้อความของแอปพลิเคชัน เมื่อปั๊มข้อความดำเนินการกับข้อความนั้นตัวควบคุมจะได้รับการอัปเดตบนเธรดเดียวกับที่สร้างขึ้น (โปรดจำไว้ว่าปั๊มข้อความจะทำงานบนเธรดหลัก)
และสำหรับภาพรวมโค้ดที่หนักหน่วงพร้อมตัวอย่างตัวแทน:
การดำเนินการข้ามเธรดไม่ถูกต้อง
// the canonical form (C# consumer)
public delegate void ControlStringConsumer(Control control, string text); // defines a delegate type
public void SetText(Control control, string text) {
if (control.InvokeRequired) {
control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text}); // invoking itself
} else {
control.Text=text; // the "functional part", executing only on the main thread
}
}
เมื่อคุณชื่นชม InvokeRequired แล้วคุณอาจต้องการพิจารณาใช้วิธีการขยายเพื่อตัดการเรียกเหล่านี้ นี้ถูกปกคลุมด้วยความสามารถในกองมากเกินคำถามทำความสะอาดรหัสที่เกลื่อนไปด้วยวิงวอนต้องใช้
นอกจากนี้ยังมีการเขียนเพิ่มเติมถึงสิ่งที่เกิดขึ้นในอดีตที่อาจเป็นที่สนใจ
การควบคุมหรือหน้าต่างวัตถุในแบบฟอร์ม Windows เป็นเพียงห่อหุ้มรอบหน้าต่าง Win32 ระบุที่จับ (บางครั้งเรียก HWND) สิ่งต่างๆส่วนใหญ่ที่คุณทำกับตัวควบคุมจะส่งผลให้เกิดการเรียกใช้ Win32 API ที่ใช้หมายเลขอ้างอิงนี้ ที่จับเป็นของเธรดที่สร้างขึ้น (โดยทั่วไปคือเธรดหลัก) และไม่ควรถูกจัดการโดยเธรดอื่น หากมีเหตุผลบางอย่างที่คุณจำเป็นต้องดำเนินการบางอย่างโดยใช้การควบคุมจากเธรดอื่นคุณสามารถใช้Invoke
เพื่อขอให้เธรดหลักดำเนินการในนามของคุณได้
ตัวอย่างเช่นหากคุณต้องการเปลี่ยนข้อความของป้ายกำกับจากเธรดผู้ปฏิบัติงานคุณสามารถดำเนินการดังนี้:
theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
this.Invoke(() => this.Enabled = true);
สิ่งที่this
อ้างถึงก็แน่นอนในเธรดปัจจุบันใช่มั้ย?
ถ้าคุณต้องการแก้ไขตัวควบคุมจะต้องทำในเธรดที่สร้างตัวควบคุม Invoke
วิธีนี้ช่วยให้คุณสามารถเรียกใช้เมธอดในเธรดที่เกี่ยวข้อง (เธรดที่เป็นเจ้าของแฮนเดิลหน้าต่างพื้นฐานของคอนโทรล)
ในตัวอย่างด้านล่าง thread1 แสดงข้อยกเว้นเนื่องจาก SetText1 พยายามแก้ไข textBox1.Text จากเธรดอื่น แต่ใน thread2 การดำเนินการใน SetText2 จะดำเนินการในเธรดที่สร้างกล่องข้อความ
private void btn_Click(object sender, EvenetArgs e)
{
var thread1 = new Thread(SetText1);
var thread2 = new Thread(SetText2);
thread1.Start();
thread2.Start();
}
private void SetText1()
{
textBox1.Text = "Test";
}
private void SetText2()
{
textBox1.Invoke(new Action(() => textBox1.Text = "Test"));
}
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });
ในทางปฏิบัติหมายความว่าผู้ร่วมประชุมได้รับการรับรองว่าจะถูกเรียกใช้ในเธรดหลัก สิ่งนี้มีความสำคัญเนื่องจากในกรณีของ windows ควบคุมหากคุณไม่อัปเดตคุณสมบัติบนเธรดหลักคุณจะไม่เห็นการเปลี่ยนแปลงหรือตัวควบคุมจะมีข้อยกเว้น
รูปแบบคือ:
void OnEvent(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(() => this.OnEvent(sender, e);
return;
}
// do stuff (now you know you are on the main thread)
}
this.Invoke(delegate)
ตรวจสอบให้แน่ใจว่าคุณกำลังเรียกผู้รับมอบสิทธิ์อาร์กิวเมนต์ไปthis.Invoke()
ที่เธรดหลัก / เธรดที่สร้างขึ้น
ฉันสามารถพูดได้ว่ากฎ Thumb ไม่สามารถเข้าถึงการควบคุมแบบฟอร์มของคุณยกเว้นจากเธรดหลัก
บรรทัดต่อไปนี้อาจเหมาะสมสำหรับการใช้ Invoke ()
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
มีสถานการณ์แม้ว่าคุณจะสร้างเธรด Threadpool (เช่นเธรดผู้ปฏิบัติงาน) ซึ่งจะทำงานบนเธรดหลัก จะไม่สร้างเธรดหลักเธรดใหม่เนื่องจากเธรดหลักพร้อมใช้งานสำหรับการประมวลผลคำแนะนำเพิ่มเติม ดังนั้นก่อนอื่นให้ตรวจสอบว่าเธรดที่ทำงานอยู่ในปัจจุบันเป็นเธรดหลักหรือไม่โดยใช้this.InvokeRequired
if คืนค่าเป็นจริงรหัสปัจจุบันกำลังทำงานบนเธรดของผู้ปฏิบัติงานดังนั้นจึงเรียกสิ่งนี้ Invoke (d, new object [] {text});
อื่นอัปเดตการควบคุม UI โดยตรง (ที่นี่คุณรับประกันได้ว่าคุณกำลังรันโค้ดบนเธรดหลัก)
หมายความว่าผู้รับมอบสิทธิ์จะทำงานบนเธรด UI แม้ว่าคุณจะเรียกใช้เมธอดนั้นจากผู้ปฏิบัติงานเบื้องหลังหรือเธรดพูลเธรด องค์ประกอบ UI มีความสัมพันธ์ของเธรด - พวกเขาชอบพูดคุยโดยตรงกับเธรดเดียวนั่นคือเธรด UI เธรด UI ถูกกำหนดให้เป็นเธรดที่สร้างอินสแตนซ์ควบคุมดังนั้นจึงเชื่อมโยงกับจุดจับหน้าต่าง แต่ทั้งหมดนั้นเป็นรายละเอียดการใช้งาน
ประเด็นสำคัญคือคุณจะเรียกเมธอดนี้จากเธรดผู้ปฏิบัติงานเพื่อให้คุณสามารถเข้าถึง UI ได้ (เพื่อเปลี่ยนค่าในเลเบล ฯลฯ ) เนื่องจากคุณไม่ได้รับอนุญาตให้ทำเช่นนั้นจากเธรดอื่นที่ไม่ใช่เธรด UI
ผู้รับมอบสิทธิ์เป็นแบบอินไลน์Action
หรือFunc<T>
. คุณสามารถประกาศผู้รับมอบสิทธิ์นอกขอบเขตของวิธีการที่คุณกำลังเรียกใช้หรือใช้lambda
นิพจน์ ( =>
); เนื่องจากคุณรันผู้รับมอบสิทธิ์ภายในเมธอดคุณจึงรันบนเธรดที่กำลังรันสำหรับหน้าต่าง / แอพพลิเคชั่นปัจจุบันซึ่งบิตเป็นตัวหนา
ตัวอย่าง Lambda
int AddFiveToNumber(int number)
{
var d = (int i => i + 5);
d.Invoke(number);
}
หมายความว่าผู้รับมอบสิทธิ์ที่คุณส่งผ่านถูกดำเนินการบนเธรดที่สร้างอ็อบเจ็กต์ Control (ซึ่งเป็นเธรด UI)
คุณต้องเรียกเมธอดนี้เมื่อแอปพลิเคชันของคุณเป็นแบบมัลติเธรดและคุณต้องการดำเนินการ UI บางอย่างจากเธรดอื่นที่ไม่ใช่เธรด UI เพราะหากคุณเพียงแค่พยายามเรียกใช้เมธอดในการควบคุมจากเธรดอื่นคุณจะได้รับ System.InvalidOperationException