0

システム内の複数のファイルを解析する独立したタスクを実行し、次のようにそれぞれのバージョンを取得したいと考えています。

public void obtainVersionList()
{

    for(int iterator = 1; iterator < list.length; iterator++) //list stores all the file names
    {
        Thread t = new Thread( () => GetVersion(ref list[iterator]) 
        //list will again store the fileVersions using GetVersion()
    }
}

ここ、

  1. 範囲外のインデックス例外が発生します。条件 iterator < list.length をチェックしたので、それはどのように可能ですか。これは、複数のスレッドが実行されているためですか?
  2. ディスク内の複数のファイルを解析するときの操作時間を最小限に抑える方法は?
4

5 に答える 5

2

変数は、iterator値ではなく参照によってキャプチャされています。これにより、すべてのスレッドが同じ変数を共有します。ラムダで使用する前に、まずループ ローカル変数にコピーします。

誰もが少なくとも一度はこれに陥ります。C# の設計者は、この決定を非常に後悔しており、変更を検討しています。

于 2012-10-09T12:11:29.337 に答える
2

並列実行については、あなたParallel.ForEach(またはTaskクラス)をお勧めします:

Parallel.ForEach(list, item => GetVersion(ref item));

使用する TPL は、通常はスレッド プールを使用して、スレッド管理を行います。ただし、別のスケジューラの実装を使用できます。一般に、スレッドの再利用は、多数のスレッドを生成するよりもコストがかかりません。

ウェストンの提案に触発されて、クリエイティブな LINQ の使用法と見なされる代替案を試しました。

static void Main(string[] args)
{
    var seq = Enumerable.Range(0, 10).ToList();
    var tasks = seq
        .Select(i => Task.Factory.StartNew(() => Foo(i)))
        .ToList(); // important, spawns the tasks
    var result = tasks.Select(t => t.Result);

    // no results are blockingly received before this
    // foreach loop
    foreach(var r in result)
    {
        Console.WriteLine(r);
    }
}

static int Foo(int i)
{
    return i;
}

入力ごとに、何かを行うものseqを作成しTask<T>ます。これらResultのタスクの は にまとめられresult、 の前に繰り返されませんforeach。このコードは、結果の順序も維持します。

サンプルは変更しませんseqlistこれは、やりたいように変更することとは異なる概念です。

于 2012-10-09T12:09:15.683 に答える
1

範囲外のインデックスの問題を解決するには、反復変数のローカル コピーを作成します。

for(int iterator = 1; iterator < list.length; iterator++) //list stores all the file names
{
     int iterator1 = iterator;
     Thread t = new Thread( () => GetVersion(ref list[iterator1]);
     //list will again store the fileVersions using GetVersion()
}

2) ディスク内の複数のファイルを解析するときの操作時間を最小限に抑える方法は?

機械的なディスクが 1 つしかない場合、これはあまり良い考えではありません。各スレッドが実行される機会が得られると、機械的なヘッドが跳ね返るだけです。ディスク I/O には単一のスレッドを使用してください。

于 2012-10-09T12:19:18.200 に答える
0

この質問を参照してください

イテレータ変数を閉じないでください。代わりに、ローカル変数を作成し、それを閉じます。

public void obtainVersionList()
{
    //list stores all the file names
    for(int iterator = 1; iterator < list.length; iterator++) 
    {
        //list will again store the fileVersions using GetVersion()
        var local = list[iterator];
        Thread t = new Thread( () => GetVersion(ref local);
    }
}
于 2012-10-09T12:17:37.760 に答える
0

複数のスレッドに同じリストを調整させてはなりません。リストがスレッドセーフでない限り、これはスレッドセーフではありません。タイプはわかりませんが、そうでList<string>はありません。

もう 1 つは、このために独自のスレッドを作成しないことです。リストが 200 個のファイルの場合、PC は 200 個のスレッドを作成して停止します。適切な数のスレッドを管理する作業は、threadpool に任せてください。

このソリューションは、.net4 があることを前提としています。

GetVersion の署名を次のように変更します。private static string GetVersion(string file)

        var tasks = new List<Task>();
        //start tasks
        foreach (var file in list)
        {
            var localFile = file; //local variable on advice of resharper
            tasks.Add(Task<string>.Factory.StartNew(() => GetVersion(localFile)));
        }
        //wait for them to complete
        Task.WaitAll(tasks.ToArray());
        //read the results
        IEnumerable<string> result = tasks.OfType<Task<string>>().Select(e => e.Result);
        //print em out for test
        foreach (var str in result)
        {
            Console.WriteLine(str);
        }
于 2012-10-09T12:24:03.437 に答える