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