Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Orleans tem um recurso que você pode usar para evitar alguma sobrecarga associada à serialização de mensagens contendo tipos imutáveis. Esta seção descreve o recurso e sua aplicação, começando com o contexto onde ele é relevante.
Serialização em Orleans
Quando se invoca um método do grain, o runtime Orleans faz uma cópia profunda dos argumentos do método e forma os pedidos a partir dessas cópias. Isso protege contra o código de chamada modificando os objetos de argumento antes que os dados passem para o grão chamado.
Se o grão chamado estiver em um silo diferente, as cópias serão eventualmente serializadas em um fluxo de bytes e enviadas pela rede para o silo de destino, onde serão desserializadas novamente em objetos. Se o grão invocado estiver no mesmo silo, as cópias são entregues diretamente ao método chamado.
Os valores de retorno são tratados da mesma maneira: primeiro copiados, depois possivelmente serializados e desserializados.
Observe que todos os três processos — cópia, serialização e desserialização — respeitam a identidade do objeto. Em outras palavras, se você passar uma lista contendo o mesmo objeto duas vezes, o lado recetor receberá uma lista com o mesmo objeto duas vezes, em vez de dois objetos com os mesmos valores.
Otimizar cópia
Em muitos casos, a cópia profunda é desnecessária. Por exemplo, considere um cenário em que um front-end da Web recebe uma matriz de bytes de seu cliente e passa essa solicitação, incluindo a matriz de bytes, para um grão para processamento. O processo de front-end não faz nada com o array depois de passá-lo para o grain; especificamente, ele não reutiliza o array para solicitações futuras. Dentro do grão, a matriz de bytes é analisada para buscar dados de entrada, mas não é modificada. O grão retorna outra matriz de bytes criada de volta para o cliente da Web e descarta a matriz imediatamente após devolvê-la. O front-end da Web passa a matriz de bytes de resultado de volta para seu cliente sem modificação.
Nesse cenário, não há necessidade de copiar as matrizes de bytes de solicitação ou resposta. Infelizmente, o Orleans tempo de execução não consegue descobrir isso automaticamente, pois não consegue determinar se o front-end da Web ou o grain modifica os arrays posteriormente. Idealmente, um mecanismo .NET indicaria que um valor não é mais modificado. Na falta disso, adicionamos Orleansmecanismos específicos: a Immutable<T> classe wrapper e o ImmutableAttribute.
Use o [Immutable] atributo para marcar um tipo, parâmetro, propriedade ou campo como imutável
Para tipos definidos pelo usuário, você pode adicionar o ImmutableAttribute ao tipo. Isso instrui o Orleans serializador a evitar copiar instâncias desse tipo. O trecho de código a seguir demonstra o uso [Immutable] para denotar um tipo imutável. Este tipo não será copiado durante a transmissão.
[Immutable]
public class MyImmutableType
{
public int MyValue { get; }
public MyImmutableType(int value)
{
MyValue = value;
}
}
Às vezes, pode não controlar o objeto; por exemplo, pode ser um List<int> que estás a enviar entre grãos. Outras vezes, partes de seus objetos podem ser imutáveis, enquanto outras não. Para estes casos, Orleans suporta opções adicionais.
As assinaturas de método podem incluir ImmutableAttribute em uma base por parâmetro:
public interface ISummerGrain : IGrain { // `values` will not be copied. ValueTask<int> Sum([Immutable] List<int> values); }Marque propriedades e campos individuais como ImmutableAttribute para prevenir cópias quando as instâncias do tipo contendo são copiadas.
[GenerateSerializer] public sealed class MyType { [Id(0), Immutable] public List<int> ReferenceData { get; set; } [Id(1)] public List<int> RunningTotals { get; set; } }
Utilize Immutable<T>
Use a Immutable<T> classe wrapper para indicar que um valor pode ser considerado imutável, ou seja, o valor subjacente não será modificado, portanto, nenhuma cópia é necessária para o compartilhamento seguro. Observe que o uso Immutable<T> implica que nem o provedor nem o destinatário do valor irão modificá-lo no futuro. É um compromisso mútuo e dual, não unilateral.
Para usar Immutable<T> em sua interface de grão, passe Immutable<T> em vez de T. Por exemplo, no cenário descrito acima, o método do grão era:
Task<byte[]> ProcessRequest(byte[] request);
Que se tornariam então:
Task<Immutable<byte[]>> ProcessRequest(Immutable<byte[]> request);
Para criar um Immutable<T>, basta usar seu construtor:
Immutable<byte[]> immutable = new(buffer);
Para obter o valor dentro do invólucro imutável, use a propriedade .Value.
byte[] buffer = immutable.Value;
Imutabilidade em Orleans
Para Orleans' propósitos, a imutabilidade é uma declaração estrita: o conteúdo do item de dados não será modificado de forma alguma que possa alterar o significado semântico do item ou interferir com outro thread acessando-o simultaneamente. A forma mais segura de garantir isso é simplesmente não modificar o item: opte por imutabilidade bitwise em vez de imutabilidade lógica.
Em alguns casos, é seguro relaxar isso para imutabilidade lógica, mas deves tomar cuidado para garantir que o código em mutação seja adequadamente seguro para execução em paralelo. Como lidar com multithreading é complexo e incomum num Orleans contexto, desaconselhamos fortemente essa abordagem e aconselhamos manter a imutabilidade a nível de bits.