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. 我得到 Index out of bounds 异常。这怎么可能,因为我已经检查了条件迭代器 < list.length。这是由于多个线程正在运行吗?
  2. 当我们解析磁盘中的多个文件时,如何最小化操作时间?
4

5 回答 5

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。此代码也确实维护了结果的顺序。

样品不修改seq。这是一个不同的概念,而不是list随心所欲地改变。

于 2012-10-09T12:09:15.683 回答
2

iterator变量是通过引用而不是值来捕获的。这使得所有线程共享相同的变量。在 lambda 中使用之前,先将其复制到循环局部变量。

每个人都至少为此而堕落一次。C# 设计者非常后悔这个决定,他们考虑改变它。

于 2012-10-09T12:11:29.337 回答
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)当我们解析磁盘中的多个文件时,如何最小化操作时间?

当您只有一个机械磁盘时,这并不是一个好主意。当每个线程都有机会运行时,您只会弹跳机械头。坚持使用单个线程进行磁盘 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>不是。

另一件事是您不应该为此创建自己的线程。如果列表是 200 个文件,您的 PC 将停止创建 200 个线程。让线程池为您管理合理数量的线程。

此解决方案假定您拥有 .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 回答