Aracılığıyla paylaş


Nasıl yapılır: PLINQ Sorgusunu İptal Etme

Aşağıdaki örneklerde PLINQ sorgusunu iptal etmenin iki yolu gösterilmektedir. İlk örnekte, çoğunlukla veri geçişi içeren bir sorgunun nasıl iptal edileceği gösterilmektedir. İkinci örnek, hesaplama açısından pahalı bir kullanıcı işlevi içeren bir sorgunun nasıl iptal edileceğini gösterir.

Not

"Yalnızca Benim Kodum" etkinleştirildiğinde, Visual Studio özel durumu oluşturan satırda kırılır ve "özel durum kullanıcı kodu tarafından işlenmez" hata iletisini görüntüler. Bu hata zararsızdır. F5 tuşuna basarak devam edebilir ve aşağıdaki örneklerde belirtilen özel durum işleme davranışını görebilirsiniz. Visual Studio'nun ilk hatada hata vermesini önlemek için Araçlar, Seçenekler, Hata Ayıklama, Genel altındaki "Yalnızca Kodum" onay kutusunun işaretini kaldırması gerekir.

Bu örnek kullanımı göstermek için tasarlanmıştır ve eşdeğer sıralı LINQ to Objects sorgusundan daha hızlı çalışmayabilir. Hızlandırma hakkında daha fazla bilgi için bkz . PLINQ'ta Hızlandırmayı Anlama.

Örnek 1

namespace PLINQCancellation_1
{
    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using static System.Console;

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

            // 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(cts);
            });

            int[]? results = null;
            try
            {
                results =
                    (from num in source.AsParallel().WithCancellation(cts.Token)
                     where num % 3 == 0
                     orderby num descending
                     select num).ToArray();
            }
            catch (OperationCanceledException e)
            {
                WriteLine(e.Message);
            }
            catch (AggregateException ae)
            {
                if (ae.InnerExceptions != null)
                {
                    foreach (Exception e in ae.InnerExceptions)
                    {
                        WriteLine(e.Message);
                    }
                }
            }

            foreach (var item in results ?? Array.Empty<int>())
            {
                WriteLine(item);
            }
            WriteLine();
            ReadKey();
        }

        static void UserClicksTheCancelButton(CancellationTokenSource cts)
        {
            // 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();
            Thread.Sleep(rand.Next(150, 500));
            cts.Cancel();
        }
    }
}
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
        Finally
            cs.Dispose()
        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

PLINQ çerçevesi tek OperationCanceledException bir öğeyi System.AggregateExceptioniçine almaz; OperationCanceledException ayrı bir yakalama bloğunda işlenmelidir. Bir veya daha fazla kullanıcı temsilcisi operationCanceledException(externalCT) oluşturursa (dış System.Threading.CancellationTokenkullanarak) ancak başka bir özel durum yoksa ve sorgu olarak AsParallel().WithCancellation(externalCT)tanımlanmışsa, PLINQ bir System.AggregateExceptionyerine tek OperationCanceledException bir (dışCT) verir. Ancak, bir kullanıcı temsilcisi bir OperationCanceledExceptionoluşturursa ve başka bir temsilci başka bir özel durum türü oluşturursa, her iki özel durum da içine AggregateExceptionalınır.

İptal etmeyle ilgili genel yönergeler aşağıdaki gibidir:

  1. Kullanıcı temsilcisi iptali gerçekleştirirseniz, PLINQ'u dış CancellationToken hakkında bilgilendirmeli ve bir OperationCanceledException(externalCT) oluşturmalısınız.

  2. İptal gerçekleşirse ve başka özel durum oluşmazsa, yerine bir OperationCanceledExceptionAggregateExceptionişleyin.

Örnek 2

Aşağıdaki örnekte, kullanıcı kodunda hesaplama açısından pahalı bir işleviniz olduğunda iptal işleminin nasıl işleneceğini gösterilmektedir.

namespace PLINQCancellation_2
{
    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using static System.Console;

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

            // 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(cts);
            });

            double[]? results = null;
            try
            {
                results =
                    (from num in source.AsParallel().WithCancellation(cts.Token)
                     where num % 3 == 0
                     select Function(num, cts.Token)).ToArray();
            }
            catch (OperationCanceledException e)
            {
                WriteLine(e.Message);
            }
            catch (AggregateException ae)
            {
                if (ae.InnerExceptions != null)
                {
                    foreach (Exception e in ae.InnerExceptions)
                        WriteLine(e.Message);
                }
            }

            foreach (var item in results ?? Array.Empty<double>())
            {
                WriteLine(item);
            }
            WriteLine();
            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 cts)
        {
            // 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();
            Thread.Sleep(rand.Next(150, 500));
            WriteLine("Press 'c' to cancel");
            if (ReadKey().KeyChar == 'c')
            {
                cts.Cancel();
            }
        }
    }
}
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
        Finally
            cs.Dispose()
        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

kullanıcı kodunda iptali işlerken, sorgu tanımında kullanmanız WithCancellation gerekmez. Ancak, sorgu performansı üzerinde hiçbir etkisi olmadığından ve iptal işleminin sorgu işleçleri ve kullanıcı kodunuz tarafından işlenmesine olanak sağladığından kullanmanız WithCancellationWithCancellation önerilir.

Sistemin yanıt verme hızını sağlamak için milisaniye başına yaklaşık bir kez iptal olup olmadığını denetlemenizi öneririz; ancak, 10 milisaniyeye kadar olan tüm süreler kabul edilebilir kabul edilir. Bu sıklığın kodunuzun performansı üzerinde olumsuz bir etkisi olmamalıdır.

Bir numaralandırıcı atıldığında, örneğin sorgu sonuçları üzerinde yinelenen bir foreach (Visual Basic'te Her İçin) döngüsünden kod çıktığında sorgu iptal edilir, ancak özel durum oluşturmaz.

Ayrıca bkz.