6

複数のバックグラウンド ワーカーが 5 秒間実行中のプロセスのタスクよりも優れたパフォーマンスを発揮するという変更はありますか? タスクは短時間実行プロセス用に設計されているという本を読んだことを覚えています。

私が尋ねる理由はこれです:

完了するのに 5 秒かかるプロセスがあり、完了するまでに 4000 のプロセスがあります。最初に私はしました:

for (int i=0; i<4000; i++) {
    Task.Factory.StartNewTask(action);
}

そして、これはパフォーマンスが低下しました (最初の 1 分後、3 ~ 4 個のタスクが完了し、コンソール アプリケーションには 35 個のスレッドがありました)。これはばかげているのかもしれませんが、スレッドプールはこの種の状況を処理すると思いました (すべてのアクションをキューに入れ、スレッドが空いたらアクションを実行します)。

2 番目のステップは、手動で Environment.ProcessorCount バックグラウンド ワーカーを実行し、すべてのアクションを ConcurentQueue に配置することでした。したがって、コードは次のようになります。

var workers = new List<BackgroundWorker>();
//initialize workers

workers.ForEach((bk) =>
{
    bk.DoWork += (s, e) =>
    {
        while (toDoActions.Count > 0)
        {
            Action a;
            if (toDoActions.TryDequeue(out a))
            {
                a();
            }
        } 
    }

    bk.RunWorkerAsync();
});

これははるかに優れたパフォーマンスを発揮しました。30 人のバックグラウンド ワーカー (最初のケースと同じ数のタスク) があった場合でも、タスクよりもはるかに優れたパフォーマンスを発揮しました。

ル:

次のようにタスクを開始します。

    public static Task IndexFile(string file)
    {
        Action<object> indexAction = new Action<object>((f) =>
        {
            Index((string)f);
        });

        return Task.Factory.StartNew(indexAction, file);
    }

Index メソッドは次のとおりです。

    private static void Index(string file)
    {
        AudioDetectionServiceReference.AudioDetectionServiceClient client = new AudioDetectionServiceReference.AudioDetectionServiceClient();

        client.IndexCompleted += (s, e) =>
            {
                if (e.Error != null)
                {
                    if (FileError != null)
                    {
                        FileError(client, 
                            new FileIndexErrorEventArgs((string)e.UserState, e.Error));
                    }
                }
                else
                {
                    if (FileIndexed != null)
                    {
                        FileIndexed(client, new FileIndexedEventArgs((string)e.UserState));
                    }
                }
            };

        using (IAudio proxy = new BassProxy())
        {
            List<int> max = new List<int>();
            if (proxy.ReadFFTData(file, out max))
            {
                while (max.Count > 0 && max.First() == 0)
                {
                    max.RemoveAt(0);
                }
                while (max.Count > 0 && max.Last() == 0)
                {
                    max.RemoveAt(max.Count - 1);
                }

                client.IndexAsync(max.ToArray(), file, file);
            }
            else
            {
                throw new CouldNotIndexException(file, "The audio proxy did not return any data for this file.");
            }
        }
    }

このメソッドは、Bass.net ライブラリを使用して、mp3 ファイルからデータを読み取ります。次に、非同期メソッドを使用して、そのデータを WCF サービスに送信します。タスクを作成する IndexFile(string file) メソッドは、for ループで 4000 回呼び出されます。FileIndexed と FileError の 2 つのイベントは処理されないため、スローされることはありません。

4

3 に答える 3

1

タスクのパフォーマンスが非常に低かったのは、小さなタスク (4000) をマウントしすぎたためです。CPU もタスクをスケジュールする必要があることに注意してください。そのため、存続期間の短いタスクを多数マウントすると、CPU に余分な負荷がかかります。詳細については、TPLの 2 番目の段落を参照してください。

.NET Framework 4 以降では、TPL はマルチスレッドおよび並列コードを記述するための推奨される方法です。ただし、すべてのコードが並列化に適しているわけではありません。たとえば、ループが各反復で少量の作業しか実行しない場合、またはループが多くの反復で実行されない場合、並列化のオーバーヘッドによりコードの実行が遅くなる可能性があります。

バックグラウンド ワーカーを使用したときは、有効なスレッドの数をProcessCountに制限していました。これにより、スケジューリングのオーバーヘッドが大幅に削減されました。

于 2014-02-13T13:27:12.910 に答える
1

やるべきことの厳密に定義されたリストがあることを考えると、私はParallelクラスを使用します (Forまたは、どちらForEachが適しているかに応じて)。さらに、構成パラメーターをこれらのメソッドのいずれかに渡して、実際に同時に実行されるタスクの数を制御できます。

        System.Threading.Tasks.Parallel.For(0, 20000, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, i =>
        {
            //do something
        });

上記のコードは 20000 回の操作を実行しますが、同時に 5 つ以上の操作を実行することはありません。

バックグラウンドワーカーの方がうまくいった理由は、最初にそれらを作成してインスタンス化したためだと思いますが、サンプルTaskコードではTask、操作ごとに新しいオブジェクトを作成しているようです。

Taskあるいは、最初にインスタンス化された固定数のオブジェクトを使用ConcurrentQueueして、バックグラウンド ワーカーで行ったのと同じようなアクションを で実行することを考えましたか? それはまた、非常に効率的であることを証明するはずです。

于 2014-02-13T13:15:39.473 に答える
0

スレッドプールの使用を検討しましたか?

http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx

スレッドの使用時にパフォーマンスが低下する場合は、スレッド化のオーバーヘッド (個々のスレッドの割り当てと破棄) が原因である可能性があります。

于 2012-05-28T14:13:18.020 に答える