web-dev-qa-db-ja.com

Parallel.ForEachブロックはありますか?

.net関数 Parallel.ForEach は呼び出しスレッドをブロックしますか?行動に関する私の推測はこれらの1つです。

  1. はい、実行中の最も遅いアイテムが戻るまでブロックします。
  2. いいえ、ブロックせず、すぐに制御を返します。並列実行する項目は、バックグラウンドスレッドで実行されます。

それとも何か他のことが起こっているのでしょうか?

この質問は、ロギングクラスでこれを実装するときに発生しました。

public class MultipleLoggingService : LoggingServiceBase
{
    private readonly List<LoggingServiceBase> loggingServices;

    public MultipleLoggingService(List<LoggingServiceBase> loggingServices)
    {
        this.loggingServices = loggingServices;
        LogLevelChanged += OnLogLevelChanged;
    }

    private void OnLogLevelChanged(object sender, LogLevelChangedArgs args)
    {
        loggingServices.ForEach(l => l.LogLevel = LogLevel);
    }

    public override LogMessageResponse LogMessage(LogMessageRequest request)
    {
        if (request.LogMessage)
            Parallel.ForEach(loggingServices, l => l.LogMessage(request));

        return new LogMessageResponse{MessageLogged = request.LogMessage};
    }
}

LogMessageメソッドが他のいくつかのロギングサービスを呼び出すことに注意してください。私はその部分がすぐに戻る必要があるので、呼び出しスレッドをブロックしません。


更新:他のユーザーからのコメントに基づく(動作が#1であることを確認しました)。したがって、私は Task ライブラリを使用するようにアドバイスを取り、ループを次のように書き直しました。

          if (request.LogMessage)
            foreach (var loggingService in loggingServices)
                Task.Factory.StartNew(() => loggingService.LogMessage(request));
49
Paul Fryer

番号1は正しいです。 Parallel.ForEachは、ループが完了するまで戻りません。その動作を望まない場合は、ループをTaskとして実行し、別のスレッドで実行できます。

57
Gabe

通常のforeach()でStartNewを更新します。

これは大規模なコレクションには最適ではない可能性があり、エラーを処理するポイントがありません。

あなたのloggingServicesはおそらく何千ものアイテムを保持していませんが、エラー処理はポイントのままです。

検討してください:

Task.Factory.StartNew(() => 
{
   try
   {
        Parallel.ForEach(loggingServices, l => l.LogMessage(request));
   }
   catch(SomeException ex)
   {
       // at least try to log it ...
   }
});
10
Henk Holterman