TNxHorizon
sepenuhnya aman untuk threadNXHorizon.Instance
, aman untuk thread dan dapat digunakan dari thread mana pun Deklarasikan jenis acara:
Acara dikategorikan berdasarkan jenis informasi - TypeInfo
. Setiap kategori acara terpisah memerlukan tipe yang berbeda.
type
TFoo = class
...
end ;
TOtherFoo = type TFoo;
TIntegerEvent = type Integer;
TStringEvent = type string;
TFooEvent = INxEvent<TFoo>;
TOtherFooEvent = INxEvent<TOtherFoo>;
Berlangganan/berhenti berlangganan acara:
Berlangganan acara dapat ditambahkan ke kelas mana pun yang ada.
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 ;
Kirim pesan:
NxHorizon.Instance.Post<TIntegerEvent>( 5 );
NxHorizon.Instance.Send<TStringEvent>( ' abc ' , Async);
atau
var
IntEvent: TIntegerEvent;
StrEvent: TStringEvent;
IntEvent := 5 ;
StrEvent := ' abc ' ;
NxHorizon.Instance.Post(IntEvent);
NxHorizon.Instance.Send(StrEvent, Async);
Metode pengendali kejadian harus sesuai dengan deklarasi berikut, di mana T
dapat berupa tipe apa pun. Pengiriman asinkron memerlukan tipe dengan manajemen memori otomatis atau tipe nilai. Anda juga dapat menggunakan instance objek berumur panjang yang dikelola secara manual sebagai peristiwa, namun dalam kasus seperti itu, Anda harus memastikan bahwa instance tersebut tidak akan dimusnahkan sebelum pesan yang sudah dikirim diproses sepenuhnya.
procedure( const aEvent: T) of object ;
Tipe TNxHorizonDelivery
mendeklarasikan empat opsi pengiriman:
Sync
- sinkron di thread saat iniAsync
- asinkron di thread latar belakang acakMainSync
- sinkron di thread utamaMainAsync
- asinkron di thread utama Sync
dan MainSync
adalah operasi PEMBLOKIRAN, dan pengendali kejadian akan segera dijalankan dalam konteks thread saat ini, atau disinkronkan dengan thread utama. Ini akan memblokir pengiriman event lain menggunakan instance bus event yang sama hingga event handler selesai. Jangan menggunakannya (atau menggunakannya dengan hemat hanya untuk eksekusi singkat) pada instance bus peristiwa default.
Jika pengiriman kejadian dilakukan dari konteks thread utama, pengiriman MainAsync
akan menggunakan TThread.ForceQueue
untuk menjalankan pengendali kejadian secara asinkron dalam konteks thread utama.
Berlangganan ke event handler akan membuat instance INxEventSubscription
baru. Anda harus menyimpan instance yang dikembalikan untuk berhenti berlangganan nanti.
Ada dua metode untuk berhenti berlangganan: Unsubscribe
dan UnsubscribeAsync
.
Kedua metode membatalkan langganan dan menghapusnya dari kumpulan langganan yang dikelola di bus peristiwa. Koleksi ini sedang diulangi di dalam metode Post
dan Send
. Modifikasi apa pun pada saat itu tidak diperbolehkan, dan dapat mengakibatkan perilaku yang tidak diharapkan.
Untuk menghindari modifikasi koleksi pelanggan selama iterasi, jika Anda ingin berhenti berlangganan dari kode yang berjalan di event handler yang dikirim secara sinkron, Anda harus menggunakan UnsubscribeAsync
, yang akan segera membatalkan langganan, namun menunda penghapusan sebenarnya dari koleksi, menjalankannya di luar mengirimkan iterasi.
Pengendali kejadian yang dikirim secara asinkron selalu berjalan di luar iterasi pengiriman, dan mengizinkan penggunaan metode Unsubscribe
. Namun, cara penangan dikirim dapat diubah dengan kode eksternal yang tidak terkait, dan jika Anda tidak dapat sepenuhnya menjamin pengiriman asinkron, penggunaan UnsubscribeAsync
dijamin.
Unsubscribe
dan UnsubscribeAsync
juga membatalkan langganan, sebelum menghapusnya dari koleksi langganan. Biasanya, tidak perlu membatalkan langganan secara eksplisit sebelum berhenti berlangganan, namun jika Anda memiliki alasan tertentu mengapa Anda ingin membatalkan langganan suatu saat sebelum berhenti berlangganan, Anda dapat memanggil metode Cancel
. Cancel
dapat dengan aman dilakukan beberapa kali. Setelah langganan dibatalkan, statusnya tidak dapat dikembalikan.
Karena pengiriman peristiwa asinkron, pengendali peristiwa mungkin sudah dikirimkan pada saat Anda membatalkan atau berhenti berlangganan langganan tertentu. Jika Anda berhenti berlangganan dari sebuah destruktor, destruktor kelas pelanggan Anda, hal ini dapat menyebabkan Anda mengakses instance pelanggan selama proses penghancurannya, atau setelah instance tersebut dihancurkan. Untuk mencegah skenario seperti itu, Anda dapat memanggil WaitFor
pada langganan, yang akan segera membatalkan langganan dan memblokir hingga semua event handler yang dikirim selesai dijalankan.
Jika Anda memanggil WaitFor
dari konteks thread utama, dan pengendali kejadian Anda berjalan dalam jangka waktu lama, hal ini akan menyebabkan aplikasi Anda berhenti merespons selama jangka waktu tersebut.
Metode BeginWork
dan EndWork
adalah bagian dari mekanisme tunggu berlangganan. Jika Anda perlu menjalankan beberapa kode di dalam event handler di beberapa thread lain, dan Anda perlu memastikan bahwa kode tersebut juga akan ditunggu, Anda dapat memanggil BeginWork
sebelum Anda memulai thread tersebut, dan EndWork
setelah selesai. Pastikan semua jalur kode pada akhirnya akan memanggil EndWork
yang cocok, karena jika tidak melakukan hal ini akan menyebabkan kebuntuan saat Anda memanggil 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);
Metode Post
digunakan untuk memposting acara di mana opsi pengiriman akan bergantung pada set opsi pengiriman langganan saat berlangganan acara.
Metode Send
mengambil alih opsi pengiriman langganan, dan mengirimkan peristiwa dengan cara yang ditentukan oleh parameter aDelivery
yang diteruskan. Jika langganan menentukan pengiriman dalam konteks thread utama, metode Send
akan memenuhi persyaratan tersebut, jadi Anda tidak perlu khawatir tentang sinkronisasi di pengendali kejadian tersebut.
Apakah Post
atau Send
akan memblokir panggilan tergantung pada opsi pengiriman yang digunakan. Saat Anda menggunakan Post
, harap perhatikan bahwa langganan berbeda untuk jenis acara yang sama dapat dikonfigurasi dengan opsi pengiriman berbeda.
TNxHorizon
adalah kelas yang dikelola secara manual dan sepenuhnya aman untuk thread. Anda dapat membuat instance bus peristiwa terpisah sebanyak yang Anda inginkan. Instance sepenuhnya aman untuk thread, dan tidak memerlukan perlindungan tambahan apa pun selama Anda menggunakan referensi dalam mode baca-saja—setelah Anda menginisialisasi referensi dan mulai menggunakan instance tersebut di seluruh thread, Anda tidak diperbolehkan mengubah variabel referensi itu sendiri . Anda dapat dengan bebas memanggil metode apa pun pada referensi tersebut dari thread mana pun.
Jika Anda perlu mendukung saluran yang berbeda (kategorisasi peristiwa tambahan), Anda dapat mencapai fungsi tersebut dengan membuat instans bus peristiwa terpisah untuk setiap saluran.
Fungsionalitas kelas TNxHorizon
tidak dapat diekspos secara langsung sebagai antarmuka karena menggunakan metode parameter yang tidak didukung untuk antarmuka.
Selain instans tunggal yang tersedia melalui NxHorizon.Instance
dimungkinkan untuk menggunakan instans bus terpisah untuk tujuan lain, dengan masa pakai yang jauh lebih singkat. Untuk menyederhanakan manajemen kehidupan instans tersebut dan menghindari akses pointer yang menggantung di lingkungan multi-threading, Anda dapat menggunakan INxHorizon
untuk menyimpan dan berbagi instans bus peristiwa tersebut dengan aman.
Hal ini juga membuka kemungkinan untuk menggunakan instance bus kejadian, yang lebih ringan sebagai mekanisme pengiriman dalam pola pengamat , di mana subjek yang dapat diamati menyimpan dan mengekspos referensi INxHorizon
-nya, yang dapat dilampirkan oleh pengamat. Saat berlangganan, pengamat harus menyimpan instance INxHorizon
tempat mereka berlangganan, sehingga mereka dapat berhenti berlangganan dengan aman meskipun subjeknya sendiri telah dirilis untuk sementara waktu.
Hal ini memungkinkan penggunaan pola pengamat dengan cara yang aman untuk thread dengan subjek yang bukan merupakan instance yang dikelola secara otomatis. Juga memegang referensi yang kuat (aman-thread) ke instance bus peristiwa alih-alih subjek secara langsung menghindari potensi siklus referensi saat menggunakan instance objek terkelola, daripada menggunakan referensi lemah yang tidak aman-thread.
INxHorizon.Instance
mengembalikan instans TNxHorizon
yang dibungkus yang dikelola secara manual oleh sebuah kontainer. Ini dapat digunakan dengan aman selama pelanggan memiliki referensi yang kuat terhadap wadahnya.
Subjek perlu memanggil metode ShutDown
pada referensi INxHorizon
selama proses pembersihannya. Ini akan menyetel tanda IsActive
ke False
dan mengirimkan TNxHorizonShutDownEvent
ke pelanggannya, sehingga mereka dapat melakukan pembersihan dengan benar. TNxHorizonShutDownEvent
berisi instance TNxHorizon
yang dibungkus, sehingga pelanggan dapat menggunakan event handler shutdown tunggal untuk mengelola beberapa subjek.
Memanggil ShutDown
tidak berdampak apa pun pada kemampuan bus untuk mengirim dan memposting pesan. Jika Anda perlu memastikan bahwa Anda tidak mengirimkan acara baru selama proses pembersihan, Anda dapat memeriksa tanda IsActive
sebelum memanggil Post
atau Send
.
Bus peristiwa ini menggunakan TTask
dari PPL untuk pengiriman peristiwa secara asinkron di XE7 dan versi Delphi yang lebih baru. Tugas-tugas tersebut berjalan di kumpulan thread default. Ini memang disengaja. Hal ini didasarkan pada premis bahwa kode apa pun yang menggunakan kumpulan thread default harus berjalan sangat cepat dan tidak menimbulkan perselisihan.
Jika Anda memiliki kode di event handler atau kode lain yang menggunakan kumpulan default untuk tugas-tugas yang berjalan lama dan dapat menyebabkan masalah, maka tindakan yang benar adalah menjalankan kode spesifik yang sudah berjalan lama tersebut pada kumpulan thread khusus yang terpisah. tentang membuat beberapa kumpulan thread di seluruh bagian yang akan melayani berbagai bagian kerangka kerja yang perlu menjalankan beberapa tugas.
Untuk pengendali peristiwa yang berjalan lama, solusi tercepat untuk masalah ini adalah menggunakan pengiriman sinkron dan memulai tugas baru di dalam kode pengendali peristiwa yang kemudian dapat menggunakan kumpulan thread non-default lainnya. Dengan begitu, Anda akan memiliki kontrol lebih besar atas kode Anda, dan kebebasan untuk mengubah perilaku penangan tertentu tanpa memengaruhi semua penangan lain yang berjalan pada instance bus peristiwa yang sama:
procedure TSubscriber.OnLongEvent ( const aEvent: TLongEvent);
begin
TTask.Run(
procedure
begin
...
end , DedicatedThreadPool);
end ;
Fitur utama implementasi bus acara ini adalah keamanan thread, kecepatan, dan kesederhanaan. Fitur dan ekstensi tambahan apa pun tidak boleh mengganggu tujuan dan maksud awal tersebut.
Implementasi ini juga didasarkan pada persyaratan dan kode saya sendiri, dan mungkin beberapa bagian tidak sepenuhnya memenuhi beberapa alur kerja kode umum lainnya.
Karena kecepatannya didasarkan pada penerapan metode Post
dan Send
saat ini, saya tidak mengharapkan banyak perubahan di area tersebut. Namun, meningkatkan atau mendukung alur kerja langganan yang berbeda di luar kedua metode tersebut dapat dilakukan.
https://dalija.prasnikar.info