方法: PLINQ クエリを取り消す
更新 : 2010 年 5 月
次の例では、PLINQ クエリを取り消す 2 とおりの方法を示します。 最初の例では、そのほとんどがデータ走査で構成されるクエリを取り消す方法を示します。 2 番目の例では、負荷の大きいユーザー関数を含むクエリを取り消す方法を示します。
![]() |
---|
[マイ コードのみ] が有効になっている場合、Visual Studio では、例外をスローする行で処理が中断され、"ユーザー コードで処理されない例外" に関するエラー メッセージが表示されます。 このエラーは問題にはなりません。F5 キーを押して、処理が中断された箇所から続行し、以下の例に示す例外処理動作を確認できます。Visual Studio による処理が最初のエラーで中断しないようにするには、[ツール] メニューの [オプション]、[デバッグ] の順にクリックし、[全般] で [マイ コードのみ] チェック ボックスをオフにします。 この例は使用法を示すことを目的としており、同等の LINQ to Objects 順次クエリよりも実行速度が遅い場合があります。高速化の詳細については、「PLINQ での高速化について」を参照してください。 |
使用例
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();
}
}
}
PLINQ のフレームワークでは、単一の OperationCanceledException は System.AggregateException に組み込まれないため、OperationCanceledException を独立した catch ブロックとして扱う必要があります。 1 つ以上のユーザー デリゲートが (外部の System.Threading.CancellationToken を使用して) OperationCanceledException(externalCT) をスローしたときにその他の例外がなく、クエリが AsParallel().WithCancellation(externalCT) として定義されている場合、PLINQ によって System.AggregateException ではなく単一の OperationCanceledException (externalCT) が発行されます。 ただし、1 つのユーザー デリゲートが OperationCanceledException をスローし、別のデリゲートがその他の例外の種類をスローした場合は、両方の例外が AggregateException に組み込まれます。
キャンセルの一般的なガイダンスを次に示します。
ユーザー デリゲートのキャンセルを実行する場合は、PLINQ に外部の CancellationToken を示して、OperationCanceledException(externalCT) をスローする必要があります。
キャンセルが発生したときに別の例外がスローされていない場合は、AggregateException ではなく、OperationCanceledException を処理する必要があります。
次の例は、ユーザー コードに負荷の大きい関数が含まれる場合に、キャンセルを処理する方法を示しています。
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();
}
}
}
キャンセルをユーザー コードで処理する場合、クエリの定義で WithCancellation<TSource> を使用する必要はありません。 ただし、WithCancellation<TSource> はクエリのパフォーマンスに影響を与えず、クエリ演算子とユーザー コードによってキャンセルを処理できるようになるため、これを使用することをお勧めします。
システムの応答性を確認するため、ミリ秒ごとに一度程度の頻度でキャンセルをチェックすることをお勧めします。ただし、適切な期間と見なされるのは最大で 10 ミリ秒です。 この頻度によってコードのパフォーマンスが低下することはありません。
たとえば、コードで、クエリ結果を反復処理している foreach (Visual Basic では For Each) ループを中断したときなど、列挙子が破棄された場合、クエリは取り消されますが、例外がスローされることはありません。
参照
参照
概念
その他の技術情報
履歴の変更
日付 |
履歴 |
理由 |
---|---|---|
2010 年 5 月 |
使用法と高速化に関する説明を 追加。 |
カスタマー フィードバック |