1

更新 2011-05-20 12:49AM: 私のアプリケーションでは、foreach は並列ソリューションよりも 25% 高速です。また、最大並列処理にコレクション数を使用しないでください。マシンのコア数に近いものを使用してください。

=

並行して実行したい IO バインド タスクがあります。フォルダ内のすべてのファイルに同じ操作を適用したい。内部的には、操作の結果、計算されたファイル情報を UI スレッドのコレクションに追加する Dispatcher.Invoke が生成されます。したがって、ある意味では、作業結果はメソッド呼び出しの副作用であり、メソッド呼び出しから直接返される値ではありません。

並列実行したいコアループです

foreach (ShellObject sf in sfcoll)
    ProcessShellObject(sf, curExeName);

このループのコンテキストは次のとおりです。

        var curExeName = Path.GetFileName(Assembly.GetEntryAssembly().Location);
        using (ShellFileSystemFolder sfcoll = ShellFileSystemFolder.FromFolderPath(_rootPath))
        {
            //This works, but is not parallel.
            foreach (ShellObject sf in sfcoll)
                ProcessShellObject(sf, curExeName);

            //This doesn't work.
            //My attempt at PLINQ.  This code never calls method ProcessShellObject.

            var query = from sf in sfcoll.AsParallel().WithDegreeOfParallelism(sfcoll.Count())
                        let p = ProcessShellObject(sf, curExeName)
                        select p;
        }

    private String ProcessShellObject(ShellObject sf, string curExeName)
    {
        String unusedReturnValueName = sf.ParsingName
        try
        {
            DesktopItem di = new DesktopItem(sf);
            //Up date DesktopItem stuff
            di.PropertyChanged += new PropertyChangedEventHandler(DesktopItem_PropertyChanged);
            ControlWindowHelper.MainWindow.Dispatcher.Invoke(
                (Action)(() => _desktopItemCollection.Add(di)));
        }
        catch (Exception ex)
        {
        }
        return unusedReturnValueName ;
    }

助けてくれてありがとう!

+トム

4

3 に答える 3

7

編集:質問の更新について。タスクが IO バウンドであることに気付きませんでした。おそらく、すべてのファイルが単一の (従来の?) ディスクからのものです。はい、それは遅くなります-並列化できないリソースに競合を導入しているため、ディスクがあちこちでシークする必要があります。

IO バウンドのタスクは、効果的に並列化できる場合もありますが、リソース自体が並列化可能かどうかによって異なります。たとえば、SSD (シーク時間がはるかに短い)は、見ている特性を完全に変える可能性があります。または、個別に遅い複数のサーバーからネットワーク経由でフェッチしている場合、IO バウンドになる可能性がありますが、単一のサーバーではありません。チャネル。


クエリを作成しましたが、使用したことがありません。クエリですべてを強制的に使用する最も簡単な方法は、Count()or ToList()、または同様のものを使用することです。ただし、より良いアプローチは次を使用することParallel.ForEachです。

var options = new ParallelOptions { MaxDegreeOfParallelism = sfcoll.Count() };
Parallel.ForEach(sfcoll, options, sf => ProcessShellObject(sf, curExeName));

ただし、そのように最大並列度を設定することが正しいアプローチであるかどうかはわかりません。うまくいくかもしれませんが、わかりません。これにアプローチする別の方法は、すべての操作をタスクとして開始し、 を指定することTaskCreationOptions.LongRunningです。

于 2011-05-20T07:06:17.020 に答える
1

最後に行を追加する必要がありますか

var results = query.ToList();
于 2011-05-20T07:06:55.420 に答える
1

LINQ 経由で作成されたクエリ オブジェクトは IEnumerable です。列挙した場合にのみ評価されます(たとえば、 foreach ループを介して):

        var query = from sf in sfcoll.AsParallel().WithDegreeOfParallelism(sfcoll.Count())
                    let p = ProcessShellObject(sf, curExeName)
                    select p;
        foreach(var q in query) 
        {
            // ....
        }
        // or:
        var results = query.ToArray(); // also enumerates query
于 2011-05-20T07:07:22.550 に答える