2011-11-11 10 views
2

私は、シリアルポートをテストしてGPSデバイスであるかどうかを判断する機能を実行するためにPLINQを使用しています。Parallel Linq - 返される最初の結果を返します

一部のシリアルポートは、有効なGPSであることがすぐに分かります。この場合、最初のテストを完了し、返されたテストにします。私は残りの結果を待っていません。

私はPLINQでこれを行うことができますか、またはタスクのバッチをスケジュールして返品を待つ必要がありますか?

+0

この正確な質問を投稿しようとしており、これが見つかりました。残念ながら、受け入れられた答えは正しくありません。これは4.0で可能ではありませんか(4.5で可能です)。 – bj0

答えて

0

さらに検討すると、明らかにFirstOrDefaultを使用してこれを解決できます。 PLINQは既定で順序付けを保持せず、バッファリングされていないクエリではすぐに戻ります。

http://msdn.microsoft.com/en-us/library/dd460677.aspx

+1

私はこの動作を見ていません。 FirstOrDefaultはPLINQクエリの最初の入力の結果を(AsUnorderedの場合でも)返しています。 – bj0

6

ここでPLINQはおそらく十分ではありません。 .Firstを使用することができますが、.NET 4ではこれが順次実行され、目的を破ります。 (will be improved in .NET 4.5

しかし、ここではTPLが最も適切な答えです。シリアルポートごとにTask<Location>を作成し、最初に正常に動作するまで待機するには、Task.WaitAnyを使用します。

これは、一連の「タスク」をスケジュールし、最初の結果を使用する簡単な方法を提供します。

+0

この問題は、それぞれのタスクに対してWaitAnyを実行する必要があること、結果を確認すること、肯定的な結果である場合は残りのタスクをキャンセルすること、または否定の場合は待機し続けます。タスクは残った。そのような一見単​​純な仕事のためのかなり複雑な論理。いい方法はありませんか? –

+0

@DavidPfefferあなたは 'BlockingCollection 'で結果を押して、そのスレッドをGetConsumingEnumerableと呼ぶことができます。 "有効な"結果を得たら、取り消しトークンをトリガーして使用します。 –

+0

どのポートも有効でない場合はどうですか? –

0

私は過去数日のためにオンとオフこのことについて考えていると私はC#4.0でこれを行うにはでPLINQに建てられた方法を見つけることができません。 FirstOrDefaultのこの質問に対する回答は、完全なPLINQクエリが完了しても(順序付けられた)最初の結果が返されるまで値を返しません。以下の極端な例の動作を示しています

var cts = new CancellationTokenSource(); 
var rnd = new ThreadLocal<Random>(() => new Random()); 

var q = Enumerable.Range(0, 11).Select(x => x).AsParallel() 
    .WithCancellation(cts.Token).WithMergeOptions(ParallelMergeOptions.NotBuffered).WithDegreeOfParallelism(10).AsUnordered() 
    .Where(i => i % 2 == 0) 
    .Select(i => 
    { 
     if(i == 0) 
      Thread.Sleep(3000); 
     else 
      Thread.Sleep(rnd.Value.Next(50, 100)); 
     return string.Format("dat {0}", i).Dump(); 
    }); 

cts.CancelAfter(5000); 

// waits until all results are in, then returns first 
q.FirstOrDefault().Dump("result"); 

私はすぐに使用可能な最初の結果を取得するための組み込みの方法が表示されていないが、私は2つの回避策を思い付くことができました。

最初に作業を行うタスクが作成され、タスクが返され、すぐに完了したPLINQクエリが返されます。その結果、タスクはすぐにそれが利用可能であるとして最初の結果を得るためにWAITANYに渡すことができます。

var cts = new CancellationTokenSource(); 
var rnd = new ThreadLocal<Random>(() => new Random()); 

var q = Enumerable.Range(0, 11).Select(x => x).AsParallel() 
    .WithCancellation(cts.Token).WithMergeOptions(ParallelMergeOptions.NotBuffered).WithDegreeOfParallelism(10).AsUnordered() 
    .Where(i => i % 2 == 0) 
    .Select(i => 
    { 
     return Task.Factory.StartNew(() => 
     { 
     if(i == 0) 
      Thread.Sleep(3000); 
     else 
      Thread.Sleep(rnd.Value.Next(50, 100)); 
     return string.Format("dat {0}", i).Dump(); 
     }); 
    }); 

cts.CancelAfter(5000); 

// returns as soon as the tasks are created 
var ts = q.ToArray(); 

// wait till the first task finishes 
var idx = Task.WaitAny(ts); 
ts[idx].Result.Dump("res"); 

これはおそらくそれを行うには恐ろしい方法です。 PLINQクエリの実際の作業は非常に速いTask.Factory.StartNewであるため、PLINQを使用するのは無意味です。 IEnumerable上のシンプルな.Select(i => Task.Factory.StartNew(...はより洗練されており、おそらくより高速です。

var cts = new CancellationTokenSource(); 
var rnd = new ThreadLocal<Random>(() => new Random()); 

var q = Enumerable.Range(0, 11).Select(x => x).AsParallel() 
    .WithCancellation(cts.Token).WithMergeOptions(ParallelMergeOptions.NotBuffered).WithDegreeOfParallelism(10).AsUnordered() 
    .Where(i => i % 2 == 0) 
    .Select(i => 
    { 
     if(i == 0) 
      Thread.Sleep(3000); 
     else 
      Thread.Sleep(rnd.Value.Next(50, 100)); 
     return string.Format("dat {0}", i).Dump(); 
    }); 

cts.CancelAfter(5000); 

var qu = new BlockingCollection<string>(); 

// ForAll blocks until PLINQ query is complete 
Task.Factory.StartNew(() => q.ForAll(x => qu.Add(x))); 

// get first result asap 
qu.Take().Dump("result"); 

この方法では、作業がPLINQを使用して行われ、そしてBlockingCollecionのテイク()になります。第二の回避策は、キュー(BlockingCollection)を使用し、それらが計算されたら、ちょうどこのキューに結果を挿入し

PLINQクエリによって挿入されるとすぐに最初の結果を返します。

これは望ましい結果を生成するが、私はそれだけで完全に.NET 4でPLINQでこれを達成するために+ WAITANY

0

単純なタスクを使用してオーバー任意の利点を持っていることを確認していません。0:

SerialPorts.      // Your IEnumerable of serial ports 
    AsParallel().AsUnordered().  // Run as an unordered parallel query 
    Where(IsGps).     // Matching the predicate IsGps (Func<SerialPort, bool>) 
    Take(1).      // Taking the first match 
    FirstOrDefault();    // And unwrap it from the IEnumerable (or null if none are found 

キーは、First or FirstOrDefaultのような順序付けられた評価を使用しないように指定するまでは使用しないでください。

関連する問題