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
بأمان عدة مرات. بمجرد إلغاء الاشتراك، لا يمكن إرجاع حالته.
بسبب إرسال الحدث غير المتزامن، من الممكن أن يكون لديك معالج حدث تم إرساله بالفعل في الوقت الذي تقوم فيه بإلغاء اشتراك معين أو إلغاء الاشتراك فيه. إذا كنت تقوم بإلغاء الاشتراك من أداة إتلاف، أي أداة إتلاف فئة المشترك، فقد يتسبب ذلك في وصولك إلى مثيل المشترك أثناء عملية التدمير الخاصة به، أو بعد تدميره. لمنع مثل هذا السيناريو، يمكنك استدعاء 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
المغلف، بحيث يمكن للمشتركين استخدام معالج حدث إيقاف التشغيل الفردي لإدارة موضوعات متعددة.
ليس لـ Calling 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