8
  • I/O 集中型の操作を行っています。
  • 一度に最大 5 つのスレッドを実行したいだけです。
  • キューに入れて完了するタスクが 8000 個あります。
  • 各タスクの実行には約 15 ~ 20 秒かかります。

ThreadPoolを見回しましたが、

        ThreadPool.SetMaxThreads(5, 0);

        List<task> tasks = GetTasks();

        int toProcess = tasks.Count;
        ManualResetEvent resetEvent = new ManualResetEvent(false);

        for (int i = 0; i < tasks.Count; i++)
        {
            ReportGenerator worker = new ReportGenerator(tasks[i].Code, id);
            ThreadPool.QueueUserWorkItem(x =>
            {
                worker.Go();
                if (Interlocked.Decrement(ref toProcess) == 0)
                    resetEvent.Set();
            });
        }

        resetEvent.WaitOne();

理由がわかりません... 私のコードは一度に 5 つ以上のスレッドを実行しています。maxthreads、setminthreads を設定しようとしましたが、5 つ以上のスレッドを実行し続けます。

何が起こっている?私は何が欠けていますか?これを別の方法で行う必要がありますか?

ありがとう

4

5 に答える 5

7

Task Parallel Library は次のことに役立ちます。

List<task> tasks = GetTasks();

Parallel.ForEach(tasks, new ParallelOptions { MaxDegreeOfParallelism = 5 }, 
  task => {ReportGenerator worker = new ReportGenerator(task.Code, id); 
           worker.Go();});

MaxDegreeOfParallelism は何をしますか?

于 2012-07-15T03:28:13.637 に答える
1

これにアプローチするには、別のより良い方法があると思います。(構文の一部を誤って Java 化してしまった場合はご容赦ください)

ここのメインスレッドには、「タスク」でやるべきことのリストがあります-タスクごとにスレッドを作成するのではなく、非常に多くのアイテムがある場合は実際には効率的ではありません。必要な数のスレッドを作成し、それらにタスクを要求させます必要に応じてリスト。

最初に行うことは、リストへのポインタとして使用するために、このコードが由来するクラスに変数を追加することです。また、必要な最大スレッド数用に 1 つ追加します。

// New variable in your class definition
private int taskStackPointer;
private final static int MAX_THREADS = 5;

リスト内の次のタスクを返し、スタック ポインターをインクリメントするメソッドを作成します。次に、このための新しいインターフェイスを作成します。

// Make sure that only one thread has access at a time
[MethodImpl(MethodImplOptions.Synchronized)] 
public task getNextTask()
{
    if( taskStackPointer < tasks.Count )
        return tasks[taskStackPointer++];
    else
        return null;
}

または、「リストの終わり」を意味する値として指定できる値がある場合は、tasks[taskStackPointer++].code を返すこともできます。ただし、おそらくこの方法の方が簡単です。

インターフェース:

public interface TaskDispatcher
{
     [MethodImpl(MethodImplOptions.Synchronized)] public task getNextTask();
}

ReportGenerator クラス内で、ディスパッチャ オブジェクトを受け入れるようにコンストラクタを変更します。

public ReportGenerator( TaskDispatcher td, int idCode )
{
    ...
}

また、 ReportGeneratorクラスを変更して、td.getNextTask()を呼び出して新しいタスクを要求し、NULL が返されたときにループを終了する外部ループが処理に含まれるようにする必要があります。

最後に、スレッド作成コードを次のように変更します (これはアイデアを提供するためのものです)。

taskStackPointer = 0;
for (int i = 0; i < MAX_THREADS; i++) 
{ 
    ReportGenerator worker = new ReportGenerator(this,id);
    worker.Go(); 
} 

そうすれば、必要な数のスレッドを作成し、それらすべてを最大容量で動作させ続けることができます。

(「[MethodImpl(MethodImplOptions.Synchronized)]」の使い方が正確かどうかはわかりません...私はC#よりもJavaに慣れています)

于 2012-07-15T03:25:41.440 に答える
1

タスク リストには 8k の項目が含まれます。これは、コードに項目を配置するように指示したためです。

List<task> tasks = GetTasks();

つまり、リストに追加したアイテムの数をデバッガーが常に表示するという意味で、この数は使用されているスレッドの数とは関係ありません。

使用中のスレッド数を判別するには、さまざまな方法があります。おそらく最も簡単な方法の 1 つは、デバッガーを使用してアプリケーションに侵入し、スレッド ウィンドウを確認することです。カウントを取得するだけでなく、各スレッドが何を行っているか (またはしていないか) を確認できます。

タスクが何を行っているか、およびスレッド プールを「調整」する数値にどのように到達したかについて、重要な議論が必要です。ほとんどのユースケースでは、スレッド プールは適切に機能します。

今、あなたの特定の質問に答えるために...

同時実行タスクの数を明示的に制御するには、タスク コレクションを List から BlockingCollection (内部的に ConcurrentQueue を使用する) に変更し、次のコードを使用して作業を「消費」する簡単な実装を検討してください。

var parallelOptions = new ParallelOptions
{
    MaxDegreeOfParallelism = 5
};

Parallel.ForEach(collection.GetConsumingEnumerable(), options, x =>
{
    // Do work here...
});

MaxDegreeOfParallelism を、実行中の作業に適していると判断した同時実行値に変更します。

以下はあなたにとって興味深いかもしれません:

Parallel.ForEach メソッド

ブロッキングコレクション

クリス

于 2012-07-15T03:55:53.263 に答える