Sistema de mensagens seguro, assíncrono e simplista para comunicação entre classes / camadas em Delphi criado pela equipe do iPub.
A Delphi possui seu próprio sistema de mensagens (System.Messaging.PAs) que funciona bem, mas é totalmente síncrono e thread inseguro. Em sistemas multithreads, sempre precisamos nos comunicar com outras classes, às vezes de maneira síncrona, às vezes de forma assíncrona, às vezes sincronizando com o MainThread (no caso da interface do usuário), e fazer isso sem um sistema de mensagens (comunicando diretamente) torna o código grande e complexo, propenso a muitos insetos.
Um sistema de mensagens ideal seria um sistema seguro de threads que permitiria que uma classe se inscrevesse e, em seguida, cancelar a inscrição para ouvir uma mensagem específica nesse período, e essa classe do ouvinte que receberá a mensagem é quem informará como o remetente invocará seu método: no mesmo thread ( postagem ), no encadeamento principal ), em outro thread ( assencras ) e em um segmento não principal. Essa é a base do nosso sistema de mensagens, o uso é semelhante a outro sistema existente, o Delphi Event Bus (DEB).
Veja a comparação em um ambiente com 1000 objetos:
| Inscreva -se | Publicar | Cair na inscrição | |
|---|---|---|---|
| iPub | 1.6368 ms | 0,1215 ms | 1.7666 MS |
| Deb | 9.8832 MS | 2.0293 ms | 4.0022 ms |
ILogOutMessage = interface
[ ' {CA101646-B801-433D-B31A-ADF7F31AC59E} ' ]
// here you can put any data
end ;Primeiro, você deve se inscrever na sua classe para ouvir as mensagens e, em seguida, todos os métodos públicos que possuem o atributo [assinando] serão inscritos.
TForm1 = class (TForm)
procedure FormCreate (Sender: TObject);
procedure FormDestroy (Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
[Subscribe(TipMessagingThread.Main)]
procedure OnLogout ( const AMessage: ILogOutMessage);
end ;
...
procedure TForm1.FormCreate (Sender: TObject);
begin
GMessaging.Subscribe(Self);
end ;
procedure TForm1.FormDestroy (Sender: TObject);
begin
GMessaging.Unsubscribe(Self);
end ;
procedure TForm1.OnLogout ( const AMessage: ILogOutMessage);
begin
Showmessage( ' Log out! ' );
end ;Você também pode declarar o método em parte protegida, mas, para fazer isso, você precisa adicionar métodos explícitos {$ rtti ([VCProtected, vcpublic, vcpublished])} antes da classe.
var
LMessage: ILogOutMessage
begin
LMessage := TLogOutMessage.Create;
GMessaging.Post(LMessage);Nos exemplos anteriores, mostramos uma mensagem de interface, mas existem 3 tipos de mensagens:
| Identidade | Parâmetro |
|---|---|
| nome (explícito) | corda |
| GUID da interface do parâmetro (implícito) | interface |
| GUID da interface do parâmetro (implícito) + nome (explícito) | interface |
Para receber mensagens com identidade de nome, basta declarar o nome no atributo do método:
[Subscribe( ' Name ' , TipMessagingThread.Main)]Para enviar mensagens com identidade de nome, basta informá -la na chamada:
GMessaging.Post( ' Name ' , LMessage);Nota: O nome explícito é insensível ao caso.
No atributo [assinando], você pode determinar como o método que recebe uma mensagem será executado:
| Tipo | Descrição |
|---|---|
| TipMessagingThread.Posting | O padrão, o método do assinante será invocado no mesmo tópico de postagem em que a postagem foi chamada |
| TipMessagingThread.Main | O método de assinante será chamado no tópico principal |
| TipMessagingThread.async | O método do assinante será invocado de forma assíncrona em um novo tópico anonnímico que não seja o tópico de postagem |
| TipMessagingThread.background | Se o thread de postagem for o encadeamento principal, o método do assinante será chamado de forma assíncrona em um novo thread anonymous, além do thread de postagem. Se o tópico de postagem não for o segmento principal, o método do assinante será invocado de forma síncrona no mesmo tópico de postagem |
Embora o uso do atributo [Subscribe] para assinar um método seja muito prático, ele é limitado porque você deve definir o nome da mensagem antes da compilação, no tempo de design, e às vezes é realmente útil definir o nome das mensagens no tempo de execução. Um exemplo, uma mensagem para um produto que foi alterado 'Product_105346_changed', esse nome dessa mensagem que só posso definir no tempo de execução, por isso adicionamos a opção de assinar / cancelar a inscrição para ouvir uma mensagem manualmente:
GMessaging.SubscribeMethod<string>( ' product_105346_changed ' , Self.OnProductChanged, TipMessagingThread.Posting);
GMessaging.UnsubscribeMethod<string>( ' product_105346_changed ' , Self.OnProductChanged);Esses dois métodos manuais são independentes dos métodos de assinatura / cancelamento de inscrição. Você pode mesclar a mesma classe com métodos inscritos automaticamente usando o atributo [Subscribe] e a chamada de inscrição / cancelamento de inscrição e, ao mesmo tempo, nesta classe, com métodos que você adicionou manualmente usando o SubscrintEmethod / Uncscriptemethod.
Outro benefício dos métodos de inscrição manual é que eles não restringem apenas os métodos públicos (embora esse problema possa ser resolvido usando a diretiva de métodos explícitos RTTI).
A idéia do sistema é apenas encaminhar mensagens, avisos, contendo ou não informações, portanto, lembre -se de que não é aconselhável colocar grandes códigos ou códigos com paradas (aguardar) dentro dos métodos inscritos para ouvir mensagens, pois isso afetaria diretamente o desempenho do sistema, mesmo nos modos assíncronos.
Outra consideração é apenas um lembrete da maneira correta de usar o TTASK de Delphi. Nunca use o TTASK para executar métodos com paradas (eventos, semáforos, ...), não foi feito para isso, seu objetivo é executar tarefas contínuas e mais simples, se sua tarefa for mais complexa, a coisa correta é usar um TTHread. Estamos avisando sobre isso, porque nosso sistema usa o TTASK da Delphi para aumentar o desempenho principalmente em ambientes mais complexos, além de economizar recursos e, se você usar o TTASK incorretamente em seus códigos, poderá fazer com que seu aplicativo congelasse ao enviar uma mensagem.
As mensagens do iPub são licenciadas no MIT e o arquivo de licença está incluído nesta pasta.