Sdílet prostřednictvím


Postupy: Zrušení PLINQ dotazu

V následujících příkladech jsou dva způsoby, jak zrušit PLINQ dotaz. První příklad ukazuje, jak zrušit dotaz, který se skládá převážně z průchodu dat. Druhý příklad ukazuje, jak zrušit dotaz, který obsahuje uživatelskou funkci, která je výpočetně náročná.

PoznámkaPoznámka

Pokud je povolená vlastnost "Pouze můj kód", Visual Studio zastaví na řádce, která vyvolala výjimku, a zobrazí chybovou zprávu s upozorněním, že výjimka není zpracována uživatelským kódem. Tato chyba je neškodná.Stiskem klávesy F5 lze pokračovat a zjistit jak jsou výjimky zpracovány. Toto je znázorněno v příkladech níže.Chcete-li zabránit Visual Studiu v zastavení na první chybě, zrušte zaškrtávací políčko "Pouze můj kód" v menu Nástroje, Možnosti ladění, Obecné.

Tento příklad je určen k prokázání využití a může běžet rychleji než ekvivalentní sekvenční LINQ dotazu objektů.Další informace o Vančurovou Principy Vančurovou v PLINQ.

Příklad

Class Program
    Private Shared Sub Main(ByVal args As String())
        Dim source As Integer() = Enumerable.Range(1, 10000000).ToArray()
        Dim cs As New CancellationTokenSource()

        ' Start a new asynchronous task that will cancel the 
        ' operation from another thread. Typically you would call
        ' Cancel() in response to a button click or some other
        ' user interface event.
        Task.Factory.StartNew(Sub()
                                  UserClicksTheCancelButton(cs)
                              End Sub)

        Dim results As Integer() = Nothing
        Try

            results = (From num In source.AsParallel().WithCancellation(cs.Token) _
                Where num Mod 3 = 0 _
                Order By num Descending _
                Select num).ToArray()
        Catch e As OperationCanceledException

            Console.WriteLine(e.Message)
        Catch ae As AggregateException

            If ae.InnerExceptions IsNot Nothing Then
                For Each e As Exception In ae.InnerExceptions
                    Console.WriteLine(e.Message)
                Next
            End If
        End Try

        If results IsNot Nothing Then
            For Each item In results
                Console.WriteLine(item)
            Next
        End If
        Console.WriteLine()

        Console.ReadKey()
    End Sub

    Private Shared Sub UserClicksTheCancelButton(ByVal cs As CancellationTokenSource)
        ' Wait between 150 and 500 ms, then cancel.
        ' Adjust these values if necessary to make
        ' cancellation fire while query is still executing.
        Dim rand As New Random()
        Thread.Sleep(rand.[Next](150, 350))
        cs.Cancel()
    End Sub
End Class
namespace PLINQCancellation_1
{
    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;

    class Program
    {
        static void Main(string[] args)
        {
            int[] source = Enumerable.Range(1, 10000000).ToArray();
            CancellationTokenSource cs = new CancellationTokenSource();

            // Start a new asynchronous task that will cancel the 
            // operation from another thread. Typically you would call
            // Cancel() in response to a button click or some other
            // user interface event.
            Task.Factory.StartNew(() =>
            {
                UserClicksTheCancelButton(cs);
            });

            int[] results = null;
            try
            {
                results = (from num in source.AsParallel().WithCancellation(cs.Token)
                           where num % 3 == 0
                           orderby num descending
                           select num).ToArray();

            }

            catch (OperationCanceledException e)
            {
                Console.WriteLine(e.Message);
            }

            catch (AggregateException ae)
            {
                if (ae.InnerExceptions != null)
                {
                    foreach (Exception e in ae.InnerExceptions)
                        Console.WriteLine(e.Message);
                }
            }

            if (results != null)
            {
                foreach (var v in results)
                    Console.WriteLine(v);
            }
            Console.WriteLine();
            Console.ReadKey();

        }

        static void UserClicksTheCancelButton(CancellationTokenSource cs)
        {
            // Wait between 150 and 500 ms, then cancel.
            // Adjust these values if necessary to make
            // cancellation fire while query is still executing.
            Random rand = new Random();
            Thread.Sleep(rand.Next(150, 350));
            cs.Cancel();
        }
    }
}

Rámec PLINQ nevypustí jedinou výjimku OperationCanceledException do System.AggregateException; výjimka OperationCanceledException musí být zpracována v samostatném bloku catch. Pokud jeden nebo více uživatelských delegátů vyvolá výjimku OperationCanceledException(externalCT) (pomocí externího tokenu System.Threading.CancellationToken), ale nebyla vyvolána žádná jiná výjimka a dotaz byl definován jako AsParallel().WithCancellation(externalCT), pak PLINQ vyvolá jednu výjimku typu OperationCanceledException (externalCT), nikoli System.AggregateException. Avšak, pokud jeden uživatelský delegát vyvolá výjimku OperationCanceledException a jiný delegát vyvolá výjimku jiného typu, pak budou obě výjimky vráceny do AggregateException.

Obecné pokyny pro zrušení je následující:

  1. Provádíte-li zrušení uživatelského delegáta, měli byste PLINQ informovat o externím tokenu CancellationToken a vyvolat výjimku OperationCanceledException(externalCT).

  2. Pokud dojde k zrušení a nejsou vyvolány žádné další výjimky, pak by měla být zpracována výjimka OperationCanceledException, spíše než AggregateException.

Následující příklad ukazuje způsob zpracování zrušení, pokud je v uživatelském kódu výpočetně náročná funkce.

Class Program2
    Private Shared Sub Main(ByVal args As String())


        Dim source As Integer() = Enumerable.Range(1, 10000000).ToArray()
        Dim cs As New CancellationTokenSource()

        ' Start a new asynchronous task that will cancel the 
        ' operation from another thread. Typically you would call
        ' Cancel() in response to a button click or some other
        ' user interface event.
        Task.Factory.StartNew(Sub()

                                  UserClicksTheCancelButton(cs)
                              End Sub)

        Dim results As Double() = Nothing
        Try

            results = (From num In source.AsParallel().WithCancellation(cs.Token) _
                Where num Mod 3 = 0 _
                Select [Function](num, cs.Token)).ToArray()
        Catch e As OperationCanceledException


            Console.WriteLine(e.Message)
        Catch ae As AggregateException
            If ae.InnerExceptions IsNot Nothing Then
                For Each e As Exception In ae.InnerExceptions
                    Console.WriteLine(e.Message)
                Next
            End If
        End Try

        If results IsNot Nothing Then
            For Each item In results
                Console.WriteLine(item)
            Next
        End If
        Console.WriteLine()

        Console.ReadKey()
    End Sub

    ' A toy method to simulate work.
    Private Shared Function [Function](ByVal n As Integer, ByVal ct As CancellationToken) As Double
        ' If work is expected to take longer than 1 ms
        ' then try to check cancellation status more
        ' often within that work.
        For i As Integer = 0 To 4
            ' Work hard for approx 1 millisecond.
            Thread.SpinWait(50000)

            ' Check for cancellation request.
            If ct.IsCancellationRequested Then
                Throw New OperationCanceledException(ct)
            End If
        Next
        ' Anything will do for our purposes.
        Return Math.Sqrt(n)
    End Function

    Private Shared Sub UserClicksTheCancelButton(ByVal cs As CancellationTokenSource)
        ' Wait between 150 and 500 ms, then cancel.
        ' Adjust these values if necessary to make
        ' cancellation fire while query is still executing.
        Dim rand As New Random()
        Thread.Sleep(rand.[Next](150, 350))
        Console.WriteLine("Press 'c' to cancel")
        If Console.ReadKey().KeyChar = "c"c Then
            cs.Cancel()

        End If
    End Sub
End Class
namespace PLINQCancellation_2
{
    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;

    class Program
    {
        static void Main(string[] args)
        {


            int[] source = Enumerable.Range(1, 10000000).ToArray();
            CancellationTokenSource cs = new CancellationTokenSource();

            // Start a new asynchronous task that will cancel the 
            // operation from another thread. Typically you would call
            // Cancel() in response to a button click or some other
            // user interface event.
            Task.Factory.StartNew(() =>
            {
                UserClicksTheCancelButton(cs);
            });

            double[] results = null;
            try
            {
                results = (from num in source.AsParallel().WithCancellation(cs.Token)
                           where num % 3 == 0
                           select Function(num, cs.Token)).ToArray();

            }


            catch (OperationCanceledException e)
            {
                Console.WriteLine(e.Message);
            }
            catch (AggregateException ae)
            {
                if (ae.InnerExceptions != null)
                {
                    foreach (Exception e in ae.InnerExceptions)
                        Console.WriteLine(e.Message);
                }
            }

            if (results != null)
            {
                foreach (var v in results)
                    Console.WriteLine(v);
            }
            Console.WriteLine();
            Console.ReadKey();

        }

        // A toy method to simulate work.
        static double Function(int n, CancellationToken ct)
        {
            // If work is expected to take longer than 1 ms
            // then try to check cancellation status more
            // often within that work.
            for (int i = 0; i < 5; i++)
            {
                // Work hard for approx 1 millisecond.
                Thread.SpinWait(50000);

                // Check for cancellation request.
                ct.ThrowIfCancellationRequested();
            }
            // Anything will do for our purposes.
            return Math.Sqrt(n);
        }

        static void UserClicksTheCancelButton(CancellationTokenSource cs)
        {
            // Wait between 150 and 500 ms, then cancel.
            // Adjust these values if necessary to make
            // cancellation fire while query is still executing.
            Random rand = new Random();
            Thread.Sleep(rand.Next(150, 350));
            Console.WriteLine("Press 'c' to cancel");
            if (Console.ReadKey().KeyChar == 'c')
                cs.Cancel();
        }
    }
}

Při zpracování zrušení uživatelského kódu, není nutné použít WithCancellation<TSource> v definici dotazu. Však doporučujeme provést toto protože WithCancellation<TSource> nemá vliv na výkon dotazu a umožňuje zrušení zpracování dotazu subjekty a kód uživatele.

Aby byla zajištěna rychlosti odezvy systému, doporučujeme kontrolovat zrušení přibližně jednou za milisekundu; avšak jakákoli hodnota do 10 milisekund je považována za přijatelnou. Tato frekvence by neměla mít negativní dopad na výkon kód.

Čítač výčtu je vyřazen, například když kód konců smyčky foreach (pro každý v jazyce Visual Basic), která je iterace výsledky dotazu z pak dotazu je zrušena, ale žádná výjimka.

Viz také

Odkaz

ParallelEnumerable

Koncepty

Paralelní LINQ (PLINQ)

Zrušení

Historie změn

Datum

Poslední dokumenty

Důvod

Květen 2010

Byla přidána poznámka týkající se používání vs. Vančurovou.

Názory zákazníků