在C#中使用多个线程

时间:2012-10-09 12:06:28

标签: c# multithreading

我想做一个解析系统中多个文件的独立任务,并按如下方式获取每个文件的版本:

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. 如何在解析磁盘中的多个文件时最小化操作时间?

5 个答案:

答案 0 :(得分: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的概念不同。

答案 1 :(得分:2)

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

每个人至少都会这样做一次。 C#设计师对这个决定感到遗憾,他们考虑改变它。

答案 2 :(得分: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.

答案 3 :(得分:0)

请参阅this question

不要关闭迭代器变量。相反,创建一个局部变量并关闭它:

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);
    }
}

答案 4 :(得分:0)

您不应让多个线程调整相同的列表。除非列表是线程安全的,否则这不是线程安全的。我不知道类型,但List<string>不是。

另一件事是你不应该为此创建自己的线程。如果列表是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);
        }