TNxHorizon
ปลอดภัยสำหรับเธรดอย่างสมบูรณ์NXHorizon.Instance
มีความปลอดภัยของเธรดและสามารถใช้ได้จากเธรดใดก็ได้ ประกาศประเภทเหตุการณ์:
เหตุการณ์จะถูกจัดหมวดหมู่ตามข้อมูลประเภท - TypeInfo
แต่ละหมวดหมู่กิจกรรมที่แยกจากกันต้องมีประเภทที่แตกต่างกัน
type
TFoo = class
...
end ;
TOtherFoo = type TFoo;
TIntegerEvent = type Integer;
TStringEvent = type string;
TFooEvent = INxEvent<TFoo>;
TOtherFooEvent = INxEvent<TOtherFoo>;
สมัครสมาชิก/ยกเลิกการสมัครเข้าร่วมกิจกรรม:
การสมัครรับข้อมูลกิจกรรมสามารถเพิ่มลงในชั้นเรียนที่มีอยู่ได้
type
TSubscriber = class
protected
// subscriptions
fIntegerSubscription: INxEventSubscription;
fStringSubscription: INxEventSubscription;
// event handlers
procedure OnIntegerEvent ( const aEvent: TIntegerEvent);
procedure OnStringEvent ( const aEvent: TStringEvent);
public
constructor Create;
destructor Destroy; override;
end ;
constructor TSubscriber.Create;
begin
fIntegerSubscription := NxHorizon.Instance.Subscribe<TIntegerEvent>(Async, OnIntegerEvent);
fStringSubscription := NxHorizon.Instance.Subscribe<TStringEvent>(Sync, OnStringEvent);
end ;
destructor TSubscriber.Destroy;
begin
fIntegerSubscription.WaitFor;
fStringSubscription.WaitFor;
NxHorizon.Instance.Unsubscribe(fIntegerSubscription);
NxHorizon.Instance.Unsubscribe(fStringSubscription);
inherited ;
end ;
procedure TSubscriber.OnIntegerEvent ( const aEvent: TIntegerEvent);
begin
Writeln(aEvent);
end ;
procedure TSubscriber.OnStringEvent ( const aEvent: TStringEvent);
begin
Writeln(aEvent);
end ;
ส่งข้อความ:
NxHorizon.Instance.Post<TIntegerEvent>( 5 );
NxHorizon.Instance.Send<TStringEvent>( ' abc ' , Async);
หรือ
var
IntEvent: TIntegerEvent;
StrEvent: TStringEvent;
IntEvent := 5 ;
StrEvent := ' abc ' ;
NxHorizon.Instance.Post(IntEvent);
NxHorizon.Instance.Send(StrEvent, Async);
วิธีการจัดการเหตุการณ์จะต้องเป็นไปตามการประกาศต่อไปนี้ โดยที่ T
สามารถเป็นประเภทใดก็ได้ การจัดส่งแบบอะซิงโครนัสต้องใช้ประเภทที่มีการจัดการหน่วยความจำอัตโนมัติหรือประเภทค่า คุณยังสามารถใช้อินสแตนซ์ออบเจ็กต์ที่มีอายุยาวนานที่ได้รับการจัดการด้วยตนเองเป็นเหตุการณ์ได้ แต่ในกรณีเช่นนี้ คุณต้องแน่ใจว่าจะไม่ถูกทำลายก่อนที่ข้อความที่ส่งไปแล้วจะได้รับการประมวลผลอย่างสมบูรณ์
procedure( const aEvent: T) of object ;
ประเภท TNxHorizonDelivery
จะประกาศตัวเลือกการจัดส่งสี่ตัวเลือก:
Sync
- ซิงโครนัสในเธรดปัจจุบันAsync
- อะซิงโครนัสในเธรดพื้นหลังแบบสุ่มMainSync
- ซิงโครนัสบนเธรดหลักMainAsync
- อะซิงโครนัสบนเธรดหลัก Sync
และ MainSync
เป็นการดำเนินการบล็อก และผู้จัดการเหตุการณ์จะดำเนินการทันทีในบริบทของเธรดปัจจุบัน หรือซิงโครไนซ์กับเธรดหลัก วิธีนี้จะบล็อกการจัดส่งเหตุการณ์อื่นๆ โดยใช้อินสแตนซ์บัสเหตุการณ์เดียวกันจนกว่าตัวจัดการเหตุการณ์จะเสร็จสิ้น อย่าใช้ (หรือใช้เฉพาะสำหรับการดำเนินการระยะสั้นเท่านั้น) บนอินสแตนซ์บัสเหตุการณ์เริ่มต้น
หากการส่งเหตุการณ์เสร็จสิ้นจากบริบทของเธรดหลัก การจัดส่ง MainAsync
จะใช้ TThread.ForceQueue
เพื่อเรียกใช้ตัวจัดการเหตุการณ์แบบอะซิงโครนัสในบริบทของเธรดหลัก
การสมัครรับตัวจัดการเหตุการณ์จะสร้างอินสแตนซ์ INxEventSubscription
ใหม่ คุณควรจัดเก็บอินสแตนซ์ที่ส่งคืนเพื่อยกเลิกการสมัครในภายหลัง
มีสองวิธีในการยกเลิก: Unsubscribe
และ UnsubscribeAsync
ทั้งสองวิธียกเลิกการสมัครสมาชิกและลบออกจากการรวบรวมการสมัครสมาชิกที่เก็บรักษาไว้ในบัสเหตุการณ์ คอลเลกชันนี้กำลังถูกทำซ้ำภายในวิธี Post
และ Send
ไม่อนุญาตให้มีการแก้ไขใดๆ ในขณะนั้น และอาจส่งผลให้เกิดการทำงานที่ไม่คาดคิดได้
เพื่อหลีกเลี่ยงการแก้ไขการรวบรวมสมาชิกในระหว่างการวนซ้ำ หากคุณต้องการยกเลิกการสมัครจากโค้ดที่ทำงานในตัวจัดการเหตุการณ์ที่จัดส่งแบบซิงโครนัส คุณควรใช้ UnsubscribeAsync
ซึ่งจะยกเลิกการสมัครสมาชิกทันที แต่ชะลอการลบจริงออกจากคอลเลกชัน โดยเรียกใช้นอก การส่งการวนซ้ำ
ตัวจัดการเหตุการณ์ที่ส่งแบบอะซิงโครนัสมักจะทำงานนอกการวนซ้ำของการจัดส่ง และอนุญาตให้ใช้เมธอด Unsubscribe
อย่างไรก็ตาม วิธีการจัดส่งตัวจัดการสามารถเปลี่ยนแปลงได้ด้วยโค้ดภายนอกที่ไม่เกี่ยวข้อง และหากคุณไม่สามารถรับประกันการจัดส่งแบบอะซิงโครนัสได้อย่างแน่นอน ก็รับประกันการใช้ UnsubscribeAsync
Unsubscribe
และ UnsubscribeAsync
ยังยกเลิกการสมัครสมาชิกก่อนที่จะลบออกจากคอลเลกชันการสมัครรับข้อมูล โดยปกติแล้ว ไม่จำเป็นต้องยกเลิกการสมัครสมาชิกอย่างชัดเจนก่อนที่จะยกเลิกการสมัคร แต่หากคุณมีเหตุผลเฉพาะบางประการว่าทำไมคุณถึงต้องการยกเลิกการสมัครรับข้อมูล ณ จุดใดจุดหนึ่งก่อนที่จะยกเลิกการสมัคร คุณสามารถเรียกใช้วิธี Cancel
สามารถ Cancel
ได้อย่างปลอดภัยหลายครั้ง เมื่อยกเลิกการสมัครสมาชิกแล้ว สถานะจะไม่สามารถเปลี่ยนกลับได้
เนื่องจากการจัดส่งเหตุการณ์แบบอะซิงโครนัส จึงเป็นไปได้ที่จะมีตัวจัดการเหตุการณ์ที่จัดส่งไปแล้วเมื่อคุณยกเลิกหรือยกเลิกการสมัครสมาชิกเฉพาะ หากคุณยกเลิกการสมัครสมาชิกจาก destructor หรือ destructor คลาสสมาชิกของคุณ สิ่งนี้อาจทำให้คุณเข้าถึงอินสแตนซ์ของสมาชิกในระหว่างกระบวนการทำลายหรือหลังจากที่ถูกทำลายไปแล้ว เพื่อป้องกันสถานการณ์ดังกล่าว คุณสามารถโทรหา WaitFor
ในการสมัครสมาชิก ซึ่งจะยกเลิกการสมัครสมาชิกทันทีและบล็อกจนกว่าตัวจัดการเหตุการณ์ที่จัดส่งทั้งหมดจะดำเนินการเสร็จสิ้น
หากคุณเรียก WaitFor
จากบริบทของเธรดหลัก และตัวจัดการเหตุการณ์ของคุณทำงานเป็นเวลานาน นี่จะทำให้แอปพลิเคชันของคุณหยุดการตอบสนองในช่วงเวลานั้น
วิธี BeginWork
และ EndWork
เป็นส่วนหนึ่งของกลไกการรอการสมัคร หากคุณต้องการรันโค้ดบางตัวในตัวจัดการเหตุการณ์ในเธรดอื่น และคุณต้องแน่ใจว่าโค้ดนั้นจะถูกรอเช่นกัน คุณสามารถเรียก BeginWork
ก่อนที่จะเริ่มเธรดดังกล่าว และ EndWork
หลังจากที่เธรดเสร็จสิ้นได้ ตรวจสอบให้แน่ใจว่าเส้นทางโค้ดทั้งหมดจะเรียก EndWork
ที่ตรงกันในที่สุด เนื่องจากการไม่ทำเช่นนั้นจะทำให้เกิดการหยุดชะงักเมื่อคุณเรียก WaitFor
procedure TSubscriber.OnLongEvent ( const aEvent: TIntegerEvent);
begin
fIntegerSubscription.BeginWork;
try
TTask.Run(
procedure
begin
try
...
finally
fIntegerSubscription.EndWork;
end ;
end );
except
fIntegerSubscription.EndWork;
raise;
end ;
end ;
procedure Post <T>( const aEvent: T);
procedure Send <T>( const aEvent: T; aDelivery: TNxHorizonDelivery);
วิธี Post
ใช้สำหรับการโพสต์กิจกรรม โดยที่ตัวเลือกการจัดส่งจะขึ้นอยู่กับตัวเลือกการจัดส่งการสมัครรับข้อมูลที่ตั้งไว้ในขณะที่สมัครรับข้อมูลกิจกรรม
วิธี Send
จะแทนที่ตัวเลือกการจัดส่งการสมัครสมาชิก และจัดส่งเหตุการณ์ในลักษณะที่กำหนดโดยพารามิเตอร์ aDelivery
ที่ส่งผ่าน หากการสมัครสมาชิกระบุการจัดส่งในบริบทของเธรดหลัก วิธี Send
จะปฏิบัติตามข้อกำหนดนั้น ดังนั้นคุณจึงไม่ต้องกังวลเกี่ยวกับการซิงโครไนซ์ในตัวจัดการเหตุการณ์เหล่านั้น
Post
หรือ Send
จะบล็อกการโทรหรือไม่นั้นขึ้นอยู่กับตัวเลือกการจัดส่งที่ใช้ เมื่อคุณใช้ Post
โปรดทราบว่าการสมัครรับข้อมูลประเภทกิจกรรมเดียวกันที่แตกต่างกันสามารถกำหนดค่าได้ด้วยตัวเลือกการจัดส่งที่แตกต่างกัน
TNxHorizon
เป็นคลาสที่ปลอดภัยสำหรับเธรดที่ได้รับการจัดการด้วยตนเอง คุณสามารถสร้างอินสแตนซ์บัสเหตุการณ์แยกกันได้มากเท่าที่คุณต้องการ อินสแตนซ์มีความปลอดภัยของเธรดโดยสมบูรณ์ และไม่ต้องการการป้องกันเพิ่มเติมใดๆ ตราบใดที่คุณใช้การอ้างอิงในโหมดอ่านอย่างเดียว เมื่อคุณเริ่มต้นการอ้างอิงและเริ่มใช้อินสแตนซ์นั้นข้ามเธรดต่างๆ คุณจะไม่ได้รับอนุญาตให้แก้ไขตัวแปรอ้างอิงเอง . คุณสามารถเรียกวิธีการใด ๆ ได้อย่างอิสระจากการอ้างอิงจากเธรดใด ๆ
หากคุณต้องการสนับสนุนช่องทางที่แตกต่างกัน (การจัดหมวดหมู่เหตุการณ์เพิ่มเติม) คุณสามารถใช้ฟังก์ชันดังกล่าวได้โดยการสร้างอินสแตนซ์บัสเหตุการณ์แยกต่างหากสำหรับแต่ละช่องทาง
ไม่สามารถเปิดเผยฟังก์ชันการทำงานของคลาส TNxHorizon
เป็นอินเทอร์เฟซได้โดยตรง เนื่องจากจะใช้วิธีแบบกำหนดพารามิเตอร์ที่ไม่รองรับอินเทอร์เฟซ
นอกจากอินสแตนซ์ซิงเกิลตันที่พร้อมใช้งานผ่าน NxHorizon.Instance
แล้ว อินสแตนซ์ยังเป็นไปได้ที่จะใช้อินสแตนซ์บัสแยกกันเพื่อวัตถุประสงค์อื่น โดยมีอายุการใช้งานสั้นกว่ามาก เพื่อลดความซับซ้อนในการจัดการชีวิตสำหรับอินสแตนซ์เหล่านั้น และหลีกเลี่ยงการเข้าถึงตัวชี้ห้อยในสภาพแวดล้อมแบบมัลติเธรด คุณสามารถใช้ INxHorizon
เพื่อเก็บและแบ่งปันอินสแตนซ์บัสเหตุการณ์ดังกล่าวได้อย่างปลอดภัย
นอกจากนี้ยังเปิดโอกาสให้ใช้อินสแตนซ์บัสเหตุการณ์ ซึ่งค่อนข้างเบาเป็นกลไกการส่งคำสั่งใน รูปแบบผู้สังเกตการณ์ โดยที่วัตถุที่สังเกตได้จะเก็บและเปิดเผยการอ้างอิง INxHorizon
ซึ่งผู้สังเกตการณ์สามารถแนบไปกับได้ เมื่อสมัครรับข้อมูล ผู้สังเกตการณ์ควรจัดเก็บอินสแตนซ์ INxHorizon
ที่พวกเขาสมัครรับข้อมูล เพื่อให้สามารถยกเลิกการสมัครรับข้อมูลได้อย่างปลอดภัย แม้ว่าเนื้อหาดังกล่าวจะได้รับการเผยแพร่ในระหว่างนี้ก็ตาม
ซึ่งช่วยให้สามารถใช้ รูปแบบผู้สังเกตการณ์ ในลักษณะที่ปลอดภัยสำหรับเธรดกับหัวข้อที่ไม่ได้รับการจัดการอินสแตนซ์โดยอัตโนมัติ นอกจากนี้ การคงการอ้างอิงที่รัดกุม (ปลอดภัยต่อเธรด) ไปยังอินสแตนซ์บัสเหตุการณ์แทนหัวเรื่องโดยตรง จะช่วยหลีกเลี่ยงวงจรการอ้างอิงที่อาจเกิดขึ้นได้โดยตรง เมื่อใช้อินสแตนซ์อ็อบเจ็กต์ที่ได้รับการจัดการ แทนที่จะใช้การอ้างอิงที่ไม่ปลอดภัยสำหรับเธรดที่ไม่ปลอดภัย
INxHorizon.Instance
ส่งคืนอินสแตนซ์ TNxHorizon
ที่ห่อไว้ซึ่งได้รับการจัดการด้วยตนเองโดยคอนเทนเนอร์ สามารถใช้งานได้อย่างปลอดภัยตราบใดที่สมาชิกมีการอ้างอิงที่ชัดเจนถึงคอนเทนเนอร์ของตน
วัตถุจำเป็นต้องเรียกใช้วิธี ShutDown
ในการอ้างอิง INxHorizon
ในระหว่างกระบวนการล้างข้อมูล การดำเนินการนี้จะตั้งค่าสถานะ IsActive
เป็น False
และส่ง TNxHorizonShutDownEvent
ไปยังสมาชิก เพื่อให้สามารถดำเนินการล้างข้อมูลได้อย่างเหมาะสม TNxHorizonShutDownEvent
มีอินสแตนซ์ TNxHorizon
ที่ห่อไว้ ดังนั้นสมาชิกจึงสามารถใช้ตัวจัดการเหตุการณ์การปิดระบบตัวเดียวเพื่อจัดการหลายหัวข้อได้
ShutDown
การโทรไม่มีผลกระทบใดๆ ต่อความสามารถของบัสในการส่งและโพสต์ข้อความ หากคุณต้องการตรวจสอบให้แน่ใจว่าคุณไม่ได้จัดส่งกิจกรรมใหม่ในระหว่างกระบวนการล้างข้อมูล คุณสามารถตรวจสอบแฟล็ก IsActive
ก่อนที่จะโทร Post
หรือ Send
บัสเหตุการณ์นี้ใช้ TTask
จาก PPL สำหรับการส่งข้อมูลเหตุการณ์แบบอะซิงโครนัสใน XE7 และเวอร์ชัน Delphi ที่ใหม่กว่า งานเหล่านั้นรันบนพูลเธรดเริ่มต้น นี่คือโดยการออกแบบ ขึ้นอยู่กับสมมติฐานที่ว่าโค้ดใดๆ ที่ใช้เธรดพูลเริ่มต้นควรทำงานเร็วมากและไม่ควรทำให้เกิดความขัดแย้ง
หากคุณมีโค้ดในตัวจัดการเหตุการณ์หรือโค้ดอื่นๆ ที่ใช้พูลเริ่มต้นสำหรับงานที่รันระยะยาวซึ่งอาจทำให้เกิดปัญหาได้ แนวทางปฏิบัติที่ถูกต้องคือการรันโค้ดเฉพาะที่รันระยะยาวบนเธรดพูลเฉพาะแยกต่างหากแทน ของการสร้างกลุ่มเธรดจำนวนมากทั่วซึ่งจะให้บริการส่วนต่างๆ ของเฟรมเวิร์กที่จำเป็นต้องรันงานบางอย่าง
สำหรับตัวจัดการเหตุการณ์ที่รันระยะยาว วิธีแก้ปัญหาที่เร็วที่สุดคือการใช้การจัดส่งแบบซิงโครนัสและการเริ่มงานใหม่ภายในโค้ดตัวจัดการเหตุการณ์ที่สามารถใช้พูลเธรดอื่นที่ไม่ใช่ค่าเริ่มต้นได้ ด้วยวิธีนี้ คุณจะสามารถควบคุมโค้ดของคุณได้มากขึ้น และมีอิสระในการเปลี่ยนแปลงพฤติกรรมของตัวจัดการเฉพาะ โดยไม่ส่งผลกระทบต่อตัวจัดการอื่นๆ ทั้งหมดที่ทำงานบนอินสแตนซ์บัสเหตุการณ์เดียวกัน:
procedure TSubscriber.OnLongEvent ( const aEvent: TLongEvent);
begin
TTask.Run(
procedure
begin
...
end , DedicatedThreadPool);
end ;
คุณสมบัติหลักของการใช้งานบัสเหตุการณ์นี้คือความปลอดภัยของเธรด ความเร็ว และความเรียบง่าย คุณลักษณะและส่วนขยายเพิ่มเติมใดๆ จะต้องไม่กระทบต่อเป้าหมายและความตั้งใจเดิมเหล่านั้น
การใช้งานนี้ยังขึ้นอยู่กับข้อกำหนดและโค้ดของฉันเองด้วย และอาจเป็นไปได้ว่าบางส่วนอาจไม่ตรงตามเวิร์กโฟลว์โค้ดทั่วไปอื่นๆ อย่างสมบูรณ์
เนื่องจากความเร็วนั้นขึ้นอยู่กับการใช้งานวิธี Post
และ Send
ในปัจจุบัน ฉันไม่คาดหวังการเปลี่ยนแปลงมากมายในพื้นที่เหล่านั้น อย่างไรก็ตาม การปรับปรุงหรือสนับสนุนเวิร์กโฟลว์การสมัครสมาชิกที่แตกต่างกันนอกเหนือจากสองวิธีดังกล่าวก็เป็นไปได้
https://dalija.prasnikar.info