2017-05-23 12 views
1

長時間実行されているバックグラウンドジョブがあるとします。各仕事は何らかの仕事をしてから、次の仕事をつかんで実行し、時間が終わるまで続けます。長期実行タスクとスレッド - パフォーマンス

これは現在、タスクを使用して実装されています。ループ内で一度に1つのジョブを実行するJobStreamがあります。負荷に応じて、5,15、または50のストリームを一度に実行できます。

JobManager

public Task Run(CancellationToken cancellationToken) { 
    var jobTasks = Enumerable 
     .Range(0, _config.BackgroundProcessor.MaximumSimultaneousJobs) 
     .Select(o => JobStream.StartNew(...,() => RunNextJob(cancellationToken), cancellationToken)); 

    return Task.WhenAll(jobTasks); 
} 

JobStream

public static Task StartNew(Func<Task> nextJobRunner, CancellationToken cancellationToken) { 
    var jobStream = new JobStream(nextJobRunner, cancellationToken); 

    return jobStream.Start(); 
} 

private Task Start() { 
    return Task.Run(async() => { 
     do { 
      await _nextJobRunner(); 
     } while (!_cancellationToken.IsCancellationRequested); 
    }); 
} 

私の質問、タスクはここで良い動きしている場合、または私はちょうどスレッドに昔ながらの方法を作成する必要がありますか?私は主にパフォーマンスについて心配しています。別の仕事が苦労しているため、仕事が縛られることなく独立して動くことを保証しています。

+0

これは本当にMicrosoftのリアクティブフレームワーク(NuGet "System.Reactive")を使用する必要があります。よりパワフルで簡単です。 – Enigmativity

+0

私はそれをチェックします、ありがとう。 –

+0

@ JoshM。それは私に[この質問](https://stackoverflow.com/q/22492383/1768303)を思い出させました。 – Noseratio

答えて

5

これは本当にMicrosoftのリアクティブフレームワーク(NuGet "System.Reactive")を使用する必要があります。よりパワフルで簡単です。

は、ここに例を示します

void Main() 
{ 
    int number_of_streams = 10; 

    IObservable<int> query = 
     Observable 
      .Range(0, number_of_streams) 
      .Select(stream_number => 
       Observable 
        .Defer(() => Observable.Start(() => nextJob(stream_number))) 
        .Repeat()) 
      .Merge(); 

    IDisposable subscription = 
     query 
      .Subscribe(x => Console.WriteLine(x)); 
} 

public int nextJob(int streamNumber) 
{ 
    Thread.Sleep(10000); 
    return streamNumber; 
} 

これは10個の同時ストリームを実行し、各ストリームにint nextJob(int streamNumber)を呼び出します。私は各ジョブで10秒の作業をシミュレートしましたが、毎秒結果が出力されます。

subscription.Dispose()を呼び出すまでこのクエリは10ストリームで永久に繰り返され、すべて停止します。

+0

答えをよろしくお願いします。この種の状況では、タスクとスレッドについてはまだ不思議です。 –

+0

@ JoshM。 - 仕事をする。スレッドは管理するのがはるかに難しいです。 – Enigmativity

+0

'Select-> Merge'ではなく' SelectMany'を使うことができます。 – bradgonesurfing

1

@Enigmativityによって提供される回答は良好です。

しかし、仕事とスレッド間の性能差について:

ジョブが長時間実行し、CPUに負荷をかけている場合は、パフォーマンスの違いはごくわずかです。

ジョブが長時間実行されているがCPUを大量に使用していない場合は、便利なためにスレッドを作成するコストが節約されるため、タスクを使用します。

ジョブが短い場合、TPLのオーバーヘッドは短時間実行されるジョブにとって重要なため、独自のキューと旧式のマルチスレッドを使用します。

旧式のマルチスレッドと比較して、タスクはバックグラウンドジョブを実行するのに便利な方法です。スレッドを作成するコストを節約できますが、このコストは多くの(数千の)スレッドを作成する必要がある場合にのみ重要です。タスクは、キューとスケジューリング、および結果と例外の追跡にいくらかのオーバーヘッドを追加しますが、これはたくさんのもの(数十万)を作成する場合にのみ重要です。これらのジョブが本当に長時間実行されている場合は、このようなことはありません。この長時間実行されている多くのジョブを処理する必要がある場合は、タスクとスレッドのパフォーマンスの違いを比較するのとは別の問題があります。

関連する問題