53

.net関数Parallel.ForEachは呼び出し元のスレッドをブロックしますか?動作に関する私の推測は、次のいずれかです。

  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であることを確認しました)。そこで、タスクライブラリを使用するようにアドバイスし、ループを次のように書き直しました。

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

2 に答える 2

68

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

于 2012-04-14T12:47:30.080 に答える
10

更新を再確認し、通常のforeach()でStartNewを実行します。

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

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

検討:

Task.Factory.StartNew(() => 
{
   try
   {
        Parallel.ForEach(loggingServices, l => l.LogMessage(request));
   }
   catch(SomeException ex)
   {
       // at least try to log it ...
   }
});
于 2012-04-14T13:49:42.477 に答える