Sdílet prostřednictvím


Vytváření asynchronních aktivit ve WF

AsyncCodeActivity poskytuje autorům aktivit základní třídu, která umožňuje odvozené aktivity implementovat asynchronní logiku spouštění. To je užitečné pro vlastní aktivity, které musí provádět asynchronní práci, aniž by blokovaly vlákno plánovače pracovního postupu a blokovaly jakékoliv aktivity, které by mohly běžet paralelně. Toto téma obsahuje přehled o tom, jak vytvořit vlastní asynchronní aktivity pomocí AsyncCodeActivity.

Použití AsyncCodeActivity

System.Activities poskytuje autorům vlastních aktivit různé základní třídy pro různé požadavky na vytváření aktivit. Každý z nich má konkrétní sémantickou sémantiku a poskytuje autor pracovního postupu (a modul runtime aktivity) odpovídající kontrakt. Aktivita založená na AsyncCodeActivity je taková aktivita, která vykonává práci asynchronně vůči vláknu plánovače a jejíž logika provádění je vyjádřena ve spravovaném kódu. V důsledku přechodu na asynchronní režim může AsyncCodeActivity během provádění způsobit okamžik nečinnosti. Vzhledem k nestálé povaze asynchronní práce AsyncCodeActivity vždy vytvoří blok bez persistentnosti po dobu trvání provádění činnosti. Tím zabráníte modulu runtime pracovního postupu zachovat instanci pracovního postupu uprostřed asynchronní práce a zároveň zabráníte uvolnění instance pracovního postupu při provádění asynchronního kódu.

Metody AsyncCodeActivity

Aktivity odvozené z AsyncCodeActivity můžou vytvořit logiku asynchronního spouštění přepsáním BeginExecute a EndExecute metodami pomocí vlastního kódu. Při zavolání modulem runtime jsou tyto metody předány .AsyncCodeActivityContext AsyncCodeActivityContextumožňuje autorovi aktivity poskytnout sdílený stav BeginExecute/ EndExecute ve vlastnosti kontextu.UserState V následujícím příkladu GenerateRandom aktivita generuje náhodné číslo asynchronně.

public sealed class GenerateRandom : AsyncCodeActivity<int>
{
    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Func<int> GetRandomDelegate = new Func<int>(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(callback, state);
    }

    protected override int EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Func<int> GetRandomDelegate = (Func<int>)context.UserState;
        return (int)GetRandomDelegate.EndInvoke(result);
    }

    int GetRandom()
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        return r.Next(1, 101);
    }
}

Předchozí ukázková aktivita je odvozena od AsyncCodeActivity<TResult> a má vysoké OutArgument<int> s názvem Result. Hodnota vrácená metodou GetRandom je extrahována a vrácena přepsáním EndExecute a tato hodnota je nastavena Result jako hodnota. Asynchronní aktivity, které nevrací výsledek, by měly být odvozeny z AsyncCodeActivity. V následujícím příkladu je definována DisplayRandom aktivita, která je odvozena z AsyncCodeActivity. Tato aktivita se podobá aktivitě GetRandom , ale místo vrácení výsledku se v konzole zobrazí zpráva.

public sealed class DisplayRandom : AsyncCodeActivity
{
    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Action GetRandomDelegate = new Action(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Action GetRandomDelegate = (Action)context.UserState;
        GetRandomDelegate.EndInvoke(result);
    }

    void GetRandom()
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        Console.WriteLine($"Random Number: {r.Next(1, 101)}");
    }
}

Všimněte si, že protože neexistuje žádná návratová hodnota, DisplayRandom používá Action místo vyvolání Func<T,TResult> svého delegáta a delegát nevrátí žádnou hodnotu.

AsyncCodeActivity také poskytuje možnost přepsání Cancel. I když BeginExecute a EndExecute jsou povinné přepsání, Cancel je volitelné a lze je přepsat, aby aktivita při zrušení nebo přerušení vyčistit svůj nevyrovnaný asynchronní stav. Pokud je vyčištění možné a AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequested je true, měla by aktivita volat MarkCanceled. Všechny výjimky vyvolané touto metodou jsou pro instanci pracovního postupu závažné.

protected override void Cancel(AsyncCodeActivityContext context)
{
    // Implement any cleanup as a result of the asynchronous work
    // being canceled, and then call MarkCanceled.
    if (context.IsCancellationRequested)
    {
        context.MarkCanceled();
    }
}

Vyvolání asynchronních metod ve třídě

Mnoho tříd v rozhraní .NET Framework poskytuje asynchronní funkce a tuto funkci lze asynchronně vyvolat pomocí AsyncCodeActivity založené aktivity. V následujícím příkladu se vytvoří aktivita, která asynchronně vytvoří soubor pomocí FileStream třídy.

public sealed class FileWriter : AsyncCodeActivity
{
    public FileWriter()
        : base()
    {
    }

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        string tempFileName = Path.GetTempFileName();
        Console.WriteLine("Writing to file: " + tempFileName);

        FileStream file = File.Open(tempFileName, FileMode.Create);

        context.UserState = file;

        byte[] bytes = UnicodeEncoding.Unicode.GetBytes("123456789");
        return file.BeginWrite(bytes, 0, bytes.Length, callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        FileStream file = (FileStream)context.UserState;

        try
        {
            file.EndWrite(result);
            file.Flush();
        }
        finally
        {
            file.Close();
        }
    }
}

Sdílení stavu mezi metodami BeginExecute a EndExecute

V předchozím příkladu byl k objektu FileStream, který byl vytvořen v BeginExecute, přistupován v EndExecute. To je možné, protože proměnná file byla předána ve vlastnosti AsyncCodeActivityContext.UserState v BeginExecute. Toto je správná metoda pro sdílení stavu mezi BeginExecute a EndExecute. Není správné použít členovou proměnnou v odvozené třídě (FileWriter v tomto případě) ke sdílení stavu mezi BeginExecute a EndExecute protože objekt aktivity může být odkazován více instancemi aktivity. Pokus o použití členské proměnné ke sdílení stavu může mít za následek, že hodnoty z jednoho ActivityInstance přepisují nebo využívají hodnoty z jiného ActivityInstance.

Přístup k hodnotám argumentů

Prostředí AsyncCodeActivity se skládá z argumentů definovaných v aktivitě. K těmto argumentům lze přistupovat z BeginExecute/EndExecute překrytí pomocí parametru AsyncCodeActivityContext. K argumentům nelze mít v delegátu přístup, ale hodnoty argumentů nebo jakákoli jiná požadovaná data lze delegátu předat pomocí jeho parametrů. V následujícím příkladu je definována náhodná aktivita generování čísel, která z argumentu Max získá inkluzivní horní mez. Hodnota argumentu se předá asynchronnímu kódu při vyvolání delegáta.

public sealed class GenerateRandomMax : AsyncCodeActivity<int>
{
    public InArgument<int> Max { get; set; }

    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Func<int, int> GetRandomDelegate = new Func<int, int>(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(Max.Get(context), callback, state);
    }

    protected override int EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Func<int, int> GetRandomDelegate = (Func<int, int>)context.UserState;
        return (int)GetRandomDelegate.EndInvoke(result);
    }

    int GetRandom(int max)
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        return r.Next(1, max + 1);
    }
}

Plánování akcí nebo podřízených aktivit pomocí AsyncCodeActivity

AsyncCodeActivity odvozené vlastní aktivity poskytují metodu pro asynchronní provádění práce ve vztahu k vláknu pracovního procesu, ale neposkytují možnost plánovat podřízené aktivity nebo akce. Asynchronní chování lze však začlenit plánováním podřízených aktivit pomocí kombinace. Asynchronní aktivitu lze vytvořit, a poté ji složit s odvozenou aktivitou Activity nebo NativeActivity pro zajištění asynchronního chování a plánování podřízených aktivit nebo akcí. Například lze vytvořit aktivitu, která je odvozena z Activity, a má jako její implementaci Sequence obsahující asynchronní aktivitu a další aktivity, které implementují logiku aktivity. Další příklady psaní aktivit pomocí Activity a NativeActivity, viz Postupy: Vytvoření aktivity a možnosti vytváření aktivit.

Viz také