Sdílet prostřednictvím


Serializace neměnných typů v Orleans

Orleans má funkci, která se dá použít, aby se zabránilo některým režijním nákladům spojeným se serializací zpráv obsahujících neměnné typy. Tato část popisuje funkci a její aplikaci počínaje kontextem, kde je relevantní.

Serializace v Orleans

Při vyvolání odstupňované Orleans metody modul runtime vytvoří hlubokou kopii argumentů metody a vytvoří požadavek z kopií. Tím se chrání před volajícím kódem, který upravuje objekty argumentu před předáním dat do volané agregace.

Pokud je volaná zrno na jiném silu, pak se kopie nakonec serializují do bajtového datového proudu a posílají se přes síť do cílového sila, kde jsou deserializovány zpět do objektů. Pokud je volané zrno na stejném silu, pak kopie jsou předány přímo volané metodě.

Návratové hodnoty jsou zpracovávány stejným způsobem: nejprve zkopírovány, pak pravděpodobně serializovány a deserializovány.

Všimněte si, že všechny tři procesy, kopírování, serializace a deserializace, respektovat identitu objektu. Jinými slovy, pokud předáte seznam, který má stejný objekt dvakrát, na přijímající straně získáte seznam se stejným objektem dvakrát, a ne se dvěma objekty se stejnými hodnotami v nich.

Optimalizace kopírování

V mnoha případech je hloubkové kopírování zbytečné. Možným scénářem je například webový front-end, který přijímá bajtové pole z klienta a předává tento požadavek, včetně pole bajtů, do agregace pro zpracování. Front-endový proces s polem nic nedělá, jakmile ho předá do zrnka; Konkrétně se pole znovu nepoužívá k přijetí budoucí žádosti. Uvnitř agregace se pole bajtů analyzuje za účelem načtení vstupních dat, ale neupraví se. Agregační interval vrátí další bajtové pole, které bylo vytvořeno, aby bylo předáno zpět webovému klientovi; zahodí pole, jakmile ho vrátí. Webový front-end předává výsledné pole bajtů zpět klientovi beze změny.

V takovém scénáři není nutné kopírovat pole bajtů požadavku ani odpovědi. Modul runtime to bohužel nedokáže zjistit sám, protože nedokáže zjistit, Orleans jestli jsou pole později upravena webovým front-endem nebo agregačním intervalem. V nejlepším ze všech možných světů bychom měli nějaký druh mechanismu .NET, který indikuje, že hodnota již není změněna; jsme pro to přidali Orleansspecifické mechanismy: Immutable<T> třídu obálky a třídu ImmutableAttribute.

Pomocí atributu [Immutable] můžete nastavit typ, parametr, vlastnost nebo pole jako neměnný.

U uživatelem definovaných typů lze tento typ ImmutableAttribute přidat. To dává serializátoru pokyn Orleans, aby se zabránilo kopírování instancí tohoto typu. Následující fragment kódu ukazuje použití [Immutable] k označení neměnného typu. Tento typ se během přenosu nezkopíruje.

[Immutable]
public class MyImmutableType
{
    public int MyValue { get; }

    public MyImmutableType(int value)
    {
        MyValue = value;
    }
}

Někdy můžete mít nad objektem kontrolu, například že se jedná o List<int> objekt, který odesíláte mezi zrny. Jindy jsou části objektů neměnné a jiné části nejsou. V těchto případech Orleans podporuje další možnosti.

  1. Podpisy metod můžou zahrnovat ImmutableAttribute jednotlivé parametry:

    public interface ISummerGrain : IGrain
    {
      // `values` will not be copied.
      ValueTask<int> Sum([Immutable] List<int> values);
    }
    
  2. Jednotlivé vlastnosti a pole lze označit, aby ImmutableAttribute se zabránilo kopírování při kopírování instancí typu obsahujícího.

    [GenerateSerializer]
    public sealed class MyType
    {
        [Id(0), Immutable]
        public List<int> ReferenceData { get; set; }
    
        [Id(1)]
        public List<int> RunningTotals { get; set; }
    }
    

Použití Immutable<T>

Třída Immutable<T> obálky slouží k označení, že hodnota může být považována za neměnnou; to znamená, že podkladová hodnota nebude změněna, takže pro bezpečné sdílení není vyžadováno žádné kopírování. Všimněte si, že použití Immutable<T> znamená, že poskytovatel hodnoty ani příjemce hodnoty ji v budoucnu nezmění; nejedná se o jednostranný závazek, ale spíše vzájemné dvojí závazek.

Chcete-li použít Immutable<T> v rozhraní zrnitosti, místo předání T, předat Immutable<T>. Například ve výše popsaném scénáři byla metoda agregační metody:

Task<byte[]> ProcessRequest(byte[] request);

To by se pak stalo:

Task<Immutable<byte[]>> ProcessRequest(Immutable<byte[]> request);

K vytvoření Immutable<T>jednoduše použijte konstruktor:

Immutable<byte[]> immutable = new(buffer);

Pokud chcete získat hodnoty uvnitř neměnné hodnoty, použijte .Value tuto vlastnost:

byte[] buffer = immutable.Value;

Neměnnost v Orleans

Pro Orleansúčely ' neměnnost je poměrně striktní prohlášení: obsah datové položky nebude upraven žádným způsobem, který by mohl změnit sémantický význam položky, nebo které by ovlivnilo jiné vlákno současně přistupující k položce. Nejbezpečnější způsob, jak zajistit, aby se tato možnost vůbec neupravuje: bitové neměnnosti, nikoli logické neměnnosti.

V některých případech je bezpečné ho uvolnit na logickou neměnnost, ale je třeba zajistit, aby mutační kód byl správně bezpečný pro přístup z více vláken. Vzhledem k tomu, že práce s multithreadingem je složitá a neobvyklá v Orleans kontextu, důrazně doporučujeme tento přístup použít a doporučujeme držet se bitové neměnnosti.