SynchronizationContext ทำอะไร


140

ในหนังสือ Programming C # มีโค้ดตัวอย่างเกี่ยวกับSynchronizationContext:

SynchronizationContext originalContext = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(delegate {
    string text = File.ReadAllText(@"c:\temp\log.txt");
    originalContext.Post(delegate {
        myTextBox.Text = text;
    }, null);
});

ฉันเป็นมือใหม่ในการตั้งกระทู้ดังนั้นโปรดตอบโดยละเอียด ก่อนอื่นฉันไม่รู้ว่าบริบทหมายถึงอะไรโปรแกรมบันทึกoriginalContextอะไรใน? และเมื่อPostเมธอดถูกไล่ออกเธรด UI จะทำอย่างไร?
ถ้าฉันถามเรื่องโง่ ๆ โปรดแก้ไขฉันด้วยขอบคุณ!

แก้ไข: ตัวอย่างเช่นถ้าฉันเขียนmyTextBox.Text = text;ใน method ความแตกต่างคืออะไร?


1
คู่มือที่ดีมีไว้เพื่อบอกว่า วัตถุประสงค์ของรูปแบบการซิงโครไนซ์ที่ใช้โดยคลาสนี้คือเพื่อให้การดำเนินการแบบอะซิงโครนัส / การซิงโครไนซ์ภายในของรันไทม์ภาษาทั่วไปทำงานได้อย่างถูกต้องกับโมเดลการซิงโครไนซ์ที่แตกต่างกัน โมเดลนี้ยังลดความซับซ้อนของข้อกำหนดบางประการที่แอปพลิเคชันที่มีการจัดการต้องปฏิบัติตามเพื่อให้ทำงานได้อย่างถูกต้องภายใต้สภาพแวดล้อมการซิงโครไนซ์ที่แตกต่างกัน
ta.speot.is

IMHO async รออยู่แล้ว
Royi Namir

7
@RoyiNamir: ใช่ แต่เดาอะไร: async/ awaitอาศัยอยู่SynchronizationContextข้างใต้
stakx - ไม่ร่วมให้ข้อมูลใน

คำตอบ:


173

SynchronizationContext ทำอะไร

พูดง่ายๆคือSynchronizationContextแสดงตำแหน่ง "ที่" โค้ดอาจถูกเรียกใช้ ผู้ได้รับมอบหมายที่ส่งผ่านไปของมันSendหรือPostวิธีการนั้นจะถูกเรียกในตำแหน่งที่ ( Postคือเวอร์ชันที่ไม่ปิดกั้น / อะซิงโครนัสของSend)

ทุกเธรดสามารถมีSynchronizationContextอินสแตนซ์ที่เกี่ยวข้องได้ ด้ายทำงานสามารถเชื่อมโยงกับบริบทตรงกันโดยการเรียกคงSynchronizationContext.SetSynchronizationContextวิธีการและบริบทปัจจุบันของด้ายทำงานสามารถสอบถามผ่านคุณสมบัติSynchronizationContext.Current

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

Windows Forms จะติดตั้งWindowsFormsSynchronizationContextบนเธรดที่สร้างฟอร์มแรก (เธรดนี้เรียกโดยทั่วไปว่า "เธรด UI") บริบทการซิงโครไนซ์ประเภทนี้จะเรียกผู้ร่วมประชุมที่ส่งผ่านไปยังเธรดนั้นทั้งหมด สิ่งนี้มีประโยชน์มากเนื่องจาก Windows Forms เช่นเดียวกับเฟรมเวิร์ก UI อื่น ๆ อนุญาตให้ใช้การควบคุมบนเธรดเดียวกับที่สร้างขึ้นเท่านั้น

จะเกิดอะไรขึ้นถ้าฉันเขียนmyTextBox.Text = text;ใน method ความแตกต่างคืออะไร?

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

นี่คือเหตุผลที่คุณต้อง "เปลี่ยนกลับ" จากเธรดของผู้ปฏิบัติงานไปยัง "เธรด UI" (ที่myTextBoxสร้างขึ้น) ก่อนการมอบหมายนั้น ๆ ทำได้ดังนี้:

  1. ในขณะที่คุณยังอยู่ในเธรด UI ให้จับภาพของ Windows Forms ไว้ที่SynchronizationContextนั่นและจัดเก็บการอ้างอิงไว้ในตัวแปร ( originalContext) เพื่อใช้ในภายหลัง คุณต้องสอบถามSynchronizationContext.Currentณ จุดนี้ หากคุณสอบถามภายในรหัสที่ส่งไปThreadPool.QueueUserWorkItemคุณอาจได้รับบริบทการซิงโครไนซ์ที่เชื่อมโยงกับเธรดผู้ปฏิบัติงานของเธรดพูล เมื่อคุณจัดเก็บข้อมูลอ้างอิงถึงบริบทของ Windows Forms แล้วคุณสามารถใช้งานได้ทุกที่และทุกเวลาเพื่อ "ส่ง" โค้ดไปยังเธรด UI

  2. เมื่อใดก็ตามที่คุณต้องการที่จะจัดการกับองค์ประกอบ UI ( แต่ไม่ได้หรืออาจจะไม่เกี่ยวกับหัวข้อ UI อีกต่อไป) บริบทการเข้าถึง Windows Forms' ประสานผ่านoriginalContextและมือปิดรหัสที่จะจัดการกับ UI ในการอย่างใดอย่างหนึ่งหรือSendPost


ข้อสังเกตและคำแนะนำสุดท้าย:

  • บริบทการซิงโครไนซ์ใดที่คุณไม่สามารถทำได้คือการบอกคุณว่าโค้ดใดต้องทำงานในตำแหน่ง / บริบทเฉพาะและโค้ดใดที่สามารถเรียกใช้งานได้ตามปกติโดยไม่ต้องส่งผ่านไปยังไฟล์SynchronizationContext. ในการตัดสินใจว่าคุณต้องทราบกฎและข้อกำหนดของเฟรมเวิร์กที่คุณกำลังเขียนโปรแกรมด้วย - Windows Forms ในกรณีนี้

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

  • หากคุณกำลังเขียนโปรแกรมกับ .NET 4.5 หรือในภายหลังคุณสามารถทำให้ชีวิตของคุณง่ายขึ้นโดยการแปลงรหัสของคุณอย่างชัดเจนว่าการใช้งานSynchronizationContext, ThreadPool.QueueUserWorkItem, control.BeginInvokeฯลฯ ไปที่ใหม่async/ awaitคำหลักและTask Parallel Library (TPL)เช่น API โดยรอบTaskและTask<TResult>ชั้นเรียน สิ่งเหล่านี้จะดูแลการจับบริบทการซิงโครไนซ์เธรด UI ในระดับสูงเริ่มต้นการดำเนินการแบบอะซิงโครนัสจากนั้นกลับเข้าสู่เธรด UI เพื่อให้คุณสามารถประมวลผลผลลัพธ์ของการดำเนินการได้


คุณบอกว่าWindows Forms เช่นเดียวกับเฟรมเวิร์ก UI อื่น ๆ อนุญาตให้ใช้การควบคุมบนเธรดเดียวกันเท่านั้น แต่หน้าต่างทั้งหมดใน Windows จะต้องเข้าถึงได้โดยเธรดเดียวกับที่สร้างขึ้น
34660

4
@ user34660: ไม่ถูกต้อง คุณสามารถมีหลายเธรดที่สร้างตัวควบคุม Windows Forms แต่แต่ละตัวควบคุมจะเชื่อมโยงกับเธรดเดียวที่สร้างขึ้นและต้องเข้าถึงได้โดยเธรดเดียวเท่านั้น การควบคุมจากเธรด UI ที่แตกต่างกันยังมีข้อ จำกัด อย่างมากในการโต้ตอบระหว่างกัน: ไม่มีใครเป็นแม่ / ลูกของอีกฝ่ายการเชื่อมโยงข้อมูลระหว่างกันไม่ได้ ฯลฯ สุดท้ายแต่ละเธรดที่สร้างการควบคุมจำเป็นต้องมีข้อความของตัวเอง วนซ้ำ (ซึ่งเริ่มต้นโดยApplication.RunIIRC) นี่เป็นหัวข้อขั้นสูงและไม่ใช่สิ่งที่ทำโดยไม่ตั้งใจ
stakx - ไม่ร่วมให้ข้อมูลอีกต่อไป

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

3
ทั้งหมดนี้พูดถึง "windows" และ "Windows windows" ทำให้ฉันค่อนข้างเวียนหัว ฉันพูดถึง "หน้าต่าง" เหล่านี้หรือไม่ ฉันไม่คิดอย่างนั้น ...
stakx - ไม่ให้ข้อมูลใน

1
@ibubi: ฉันไม่แน่ใจว่าฉันเข้าใจคำถามของคุณ บริบทการซิงโครไนซ์ของเธรดใด ๆ ไม่ได้ถูกตั้งค่า ( null) หรืออินสแตนซ์ของSynchronizationContext(หรือคลาสย่อยของเธรด) ประเด็นของคำพูดนั้นไม่ใช่สิ่งที่คุณได้รับ แต่สิ่งที่คุณจะไม่ได้รับ: บริบทการซิงโครไนซ์เธรด UI
stakx - ไม่ร่วมให้ข้อมูลใน

26

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

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

นี่คือคำอธิบายที่ดีมากของสิ่งที่แวดล้อมประสานคือ มันเป็นเรื่องของ SynchronizationContext


16

มันเก็บผู้ให้บริการการซิงโครไนซ์คลาสที่ได้มาจาก SynchronizationContext ในกรณีนี้อาจเป็นอินสแตนซ์ของ WindowsFormsSynchronizationContext คลาสนั้นใช้เมธอด Control.Invoke () และ ControlBeginInvoke () เพื่อใช้เมธอด Send () และ Post () หรืออาจเป็น DispatcherSynchronizationContext ก็ใช้ Dispatcher.Invoke () และ BeginInvoke () ในแอป Winforms หรือ WPF ผู้ให้บริการนั้นจะได้รับการติดตั้งโดยอัตโนมัติทันทีที่คุณสร้างหน้าต่าง

เมื่อคุณรันโค้ดบนเธรดอื่นเช่นเธรดพูลเธรดที่ใช้ในข้อมูลโค้ดคุณต้องระวังว่าคุณจะไม่ใช้อ็อบเจ็กต์ที่เธรดไม่ปลอดภัยโดยตรง เช่นเดียวกับออบเจ็กต์ส่วนต่อประสานผู้ใช้ใด ๆ คุณต้องอัพเดตคุณสมบัติ TextBox.Text จากเธรดที่สร้างกล่องข้อความ วิธีการ Post () ทำให้แน่ใจว่าเป้าหมายผู้รับมอบสิทธิ์ทำงานบนเธรดนั้น

ระวังว่าตัวอย่างข้อมูลนี้อันตรายเล็กน้อยจะทำงานได้ถูกต้องก็ต่อเมื่อคุณเรียกมันจากเธรด UI SynchronizationContext.Current มีค่าต่างกันในเธรดที่แตกต่างกัน เฉพาะเธรด UI เท่านั้นที่มีค่าที่ใช้งานได้ และเป็นเหตุผลที่โค้ดต้องคัดลอก วิธีที่อ่านง่ายและปลอดภัยยิ่งขึ้นในแอป Winforms:

    ThreadPool.QueueUserWorkItem(delegate {
        string text = File.ReadAllText(@"c:\temp\log.txt");
        myTextBox.BeginInvoke(new Action(() => {
            myTextBox.Text = text;
        }));
    });

ซึ่งมีข้อดีคือทำงานเมื่อเรียกจากเธรดใด ๆ ข้อดีของการใช้ SynchronizationContext.Current คือมันยังคงใช้งานได้ไม่ว่าจะใช้รหัสใน Winforms หรือ WPF แต่ก็มีความสำคัญในไลบรารี นี่ไม่ใช่อย่างแน่นอนตัวอย่างที่ดีของโค้ดดังกล่าวคุณมักจะรู้ว่า TextBox ประเภทใดที่คุณมีอยู่ที่นี่ดังนั้นคุณจึงรู้อยู่เสมอว่าจะใช้ ControlBeginInvoke หรือ Dispatcher BeginInvoke จริงๆแล้วใช้ SynchronizationContext ปัจจุบันไม่ใช่เรื่องธรรมดา

หนังสือเล่มนี้พยายามสอนคุณเกี่ยวกับการทำเธรดดังนั้นการใช้ตัวอย่างที่มีข้อบกพร่องนี้จึงไม่เป็นไร ในชีวิตจริงในไม่กี่กรณีที่คุณอาจพิจารณาใช้ SynchronizationContext ปัจจุบันคุณยังคงปล่อยให้คำหลัก async / await ของ C # หรือ TaskScheduler.FromCurrentSynchronizationContext () ทำเพื่อคุณ แต่โปรดทราบว่าพวกเขายังคงใช้งานไม่ถูกต้องตามที่ตัวอย่างข้อมูลทำเมื่อคุณใช้ในเธรดที่ไม่ถูกต้องด้วยเหตุผลเดียวกัน คำถามที่พบบ่อยมากในที่นี้ระดับนามธรรมพิเศษมีประโยชน์ แต่ทำให้ยากที่จะเข้าใจว่าเหตุใดจึงทำงานไม่ถูกต้อง หวังว่าหนังสือเล่มนี้จะบอกคุณด้วยว่าห้ามใช้เมื่อไหร่ :)


ฉันขอโทษทำไมการจัดการเธรด UI จึงปลอดภัยต่อเธรด เช่นฉันคิดว่าเธรด UI อาจใช้ myTextBox เมื่อโพสต์ () เริ่มทำงานปลอดภัยหรือไม่?
cloudyFan

4
ภาษาอังกฤษของคุณยากที่จะถอดรหัส ตัวอย่างข้อมูลต้นฉบับของคุณจะทำงานได้อย่างถูกต้องเมื่อถูกเรียกจากเธรด UI เท่านั้น ซึ่งเป็นกรณีที่พบบ่อยมาก. จากนั้นจะโพสต์กลับไปที่เธรด UI หากถูกเรียกจากเธรดผู้ปฏิบัติงานเป้าหมายผู้รับมอบสิทธิ์ Post () จะทำงานบนเธรดพูลเธรด Kaboom นี่คือสิ่งที่คุณอยากลองด้วยตัวคุณเอง เริ่มต้นเธรดและปล่อยให้เธรดเรียกรหัสนี้ คุณทำถูกแล้วถ้าโค้ดขัดข้องด้วย NullReferenceException
Hans Passant

5

วัตถุประสงค์ของบริบทการซิงโครไนซ์ที่นี่คือเพื่อให้แน่ใจว่าmyTextbox.Text = text;ได้ถูกเรียกบนเธรด UI หลัก

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

สิ่งนี้ทำคือบันทึกบริบทการซิงโครไนซ์ก่อนสร้างเธรดพื้นหลังจากนั้นเธรดพื้นหลังจะใช้บริบทวิธีการโพสต์รันโค้ด GUI

ใช่รหัสที่คุณแสดงนั้นไร้ประโยชน์โดยพื้นฐานแล้ว ทำไมต้องสร้างเธรดพื้นหลังเพียง แต่ต้องกลับไปที่เธรด UI หลักทันที เป็นเพียงตัวอย่างเท่านั้น


4
"ใช่รหัสที่คุณแสดงนั้นไม่มีประโยชน์โดยทั่วไปทำไมต้องสร้างเธรดพื้นหลังเพียง แต่ต้องกลับไปที่เธรด UI หลักทันทีนี่เป็นเพียงตัวอย่างเท่านั้น" - การอ่านจากไฟล์อาจเป็นงานที่ยาวนานหากไฟล์มีขนาดใหญ่สิ่งที่อาจบล็อกเธรด UI และทำให้ไม่ตอบสนอง
Yair Nevet

ฉันมีคำถามโง่ ๆ ทุกเธรดมี Id และฉันคิดว่าเธรด UI มี ID = 2 ด้วยเช่นกัน จากนั้นเมื่อฉันอยู่ในเธรดพูลเธรดฉันสามารถทำสิ่งนั้นได้ไหม: var thread = GetThread (2); thread.Execute (() => textbox1.Text = "foo")?
John

@ จอห์น - ไม่ฉันไม่คิดว่าจะได้ผลเพราะเธรดกำลังดำเนินการอยู่แล้ว คุณไม่สามารถเรียกใช้เธรดที่ดำเนินการอยู่แล้วได้ Execute ใช้งานได้เฉพาะเมื่อเธรดไม่ทำงาน (IIRC)
Erik Funkenbusch

4

ไปยังแหล่งที่มา

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

SynchronizationContext ช่วยให้คุณสามารถจัดคิวงานไปยังบริบทอื่นได้ โปรดทราบว่าทุกเธรดสามารถมี SynchronizatonContext ของตัวเองได้

ตัวอย่างเช่นสมมติว่าคุณมีเธรด 2 เธรด 1 และเธรด 2 สมมติว่า Thread1 กำลังทำงานบางอย่างแล้ว Thread1 ต้องการรันโค้ดบน Thread2 วิธีหนึ่งที่ทำได้คือถาม Thread2 สำหรับอ็อบเจ็กต์ SynchronizationContext ส่งให้กับ Thread1 จากนั้น Thread1 สามารถเรียก SynchronizationContext ส่งเพื่อรันโค้ดบน Thread2


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

3

SynchronizationContext ให้เรามีวิธีการอัปเดต UI จากเธรดอื่น (ซิงโครนัสผ่านวิธีการส่งหรือแบบอะซิงโครนัสผ่านวิธีการโพสต์)

ดูตัวอย่างต่อไปนี้:

    private void SynchronizationContext SyncContext = SynchronizationContext.Current;
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Thread thread = new Thread(Work1);
        thread.Start(SyncContext);
    }

    private void Work1(object state)
    {
        SynchronizationContext syncContext = state as SynchronizationContext;
        syncContext.Post(UpdateTextBox, syncContext);
    }

    private void UpdateTextBox(object state)
    {
        Thread.Sleep(1000);
        string text = File.ReadAllText(@"c:\temp\log.txt");
        myTextBox.Text = text;
    }

SynchronizationContext.Current จะส่งคืนบริบทการซิงค์ของเธรด UI ฉันจะรู้ได้อย่างไร? เมื่อเริ่มต้นทุกฟอร์มหรือแอป WPF บริบทจะถูกตั้งค่าบนเธรด UI หากคุณสร้างแอป WPF และเรียกใช้ตัวอย่างของฉันคุณจะเห็นว่าเมื่อคุณคลิกปุ่มมันจะเข้าสู่โหมดสลีปประมาณ 1 วินาทีจากนั้นจะแสดงเนื้อหาของไฟล์ คุณอาจคาดหวังว่าจะไม่เป็นเพราะผู้เรียกเมธอด UpdateTextBox (ซึ่งก็คือ Work1) เป็นวิธีที่ส่งผ่านไปยังเธรดดังนั้นจึงควรพักเธรดนั้นไม่ใช่เธรด UI หลักไม่ใช่ แม้ว่าเมธอด Work1 จะถูกส่งผ่านไปยังเธรด แต่โปรดสังเกตว่ามันยอมรับอ็อบเจ็กต์ซึ่งเป็น SyncContext ด้วย หากคุณดูคุณจะเห็นว่าเมธอด UpdateTextBox ถูกเรียกใช้ผ่านเมธอด syncContext.Post ไม่ใช่เมธอด Work1 ดูสิ่งต่อไปนี้:

private void Button_Click(object sender, RoutedEventArgs e) 
{
    Thread.Sleep(1000);
    string text = File.ReadAllText(@"c:\temp\log.txt");
    myTextBox.Text = text;
}

ตัวอย่างสุดท้ายและตัวอย่างนี้ดำเนินการเหมือนกัน ทั้งสองไม่ปิดกั้น UI ในขณะที่ทำงาน

สรุปแล้วคิดว่า SynchronizationContext เป็นเธรด ไม่ใช่เธรด แต่เป็นการกำหนดเธรด (โปรดทราบว่าเธรดทั้งหมดไม่ได้มี SyncContext) เมื่อใดก็ตามที่เราเรียกใช้วิธีการโพสต์หรือส่งเพื่ออัปเดต UI ก็เหมือนกับการอัปเดต UI ตามปกติจากเธรด UI หลัก หากด้วยเหตุผลบางประการคุณจำเป็นต้องอัปเดต UI จากเธรดอื่นตรวจสอบให้แน่ใจว่าเธรดมี SyncContext ของเธรด UI หลักและเพียงแค่เรียกใช้วิธีการส่งหรือโพสต์ด้วยวิธีการที่คุณต้องการดำเนินการเท่านี้คุณก็พร้อมแล้ว ชุด.

หวังว่านี่จะช่วยคุณได้เพื่อน!


2

SynchronizationContextโดยทั่วไปเป็นผู้ให้บริการเรียกกลับผู้รับมอบสิทธิ์การดำเนินการส่วนใหญ่รับผิดชอบในการรับรองว่าผู้รับมอบสิทธิ์ถูกเรียกใช้ในบริบทการดำเนินการที่กำหนดหลังจากส่วนหนึ่งของโค้ด(รวมอยู่ใน Task obj ของ. Net TPL)ของโปรแกรมเสร็จสิ้นการดำเนินการแล้ว

จากมุมมองทางเทคนิค SC เป็นคลาส C # อย่างง่ายที่มุ่งเน้นเพื่อสนับสนุนและจัดเตรียมฟังก์ชันเฉพาะสำหรับอ็อบเจ็กต์ Task Parallel Library

แอปพลิเคชัน. Net ทุกตัวยกเว้นแอปพลิเคชันคอนโซลมีการนำคลาสนี้ไปใช้งานโดยเฉพาะตามกรอบงานพื้นฐานที่เฉพาะเจาะจงเช่น WPF, WindowsForm, Asp Net, Silverlight, ecc ..

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

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


1

ตัวอย่างนี้มาจากตัวอย่าง Linqpad จาก Joseph Albahari แต่มันช่วยได้มากในการทำความเข้าใจบริบทการซิงโครไนซ์

void WaitForTwoSecondsAsync (Action continuation)
{
    continuation.Dump();
    var syncContext = AsyncOperationManager.SynchronizationContext;
    new Timer (_ => syncContext.Post (o => continuation(), _)).Change (2000, -1);
}

void Main()
{
    Util.CreateSynchronizationContext();
    ("Waiting on thread " + Thread.CurrentThread.ManagedThreadId).Dump();
    for (int i = 0; i < 10; i++)
        WaitForTwoSecondsAsync (() => ("Done on thread " + Thread.CurrentThread.ManagedThreadId).Dump());
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.