不可靠的并行循环在400次中失败4次

时间:2012-06-20 12:46:32

标签: c# asp.net-mvc parallel-processing

我有一个并行foreach函数,它创建一个类的新实例,它操纵图片并将其保存到磁盘......

然而,大约4次中400次,图片被保存到磁盘,但没有被操纵,我的理论是,当它发生时,我的类中存在的一些属性是空的,当他们不支持时。 ..

4个(有时3个)错误主要发生在并行循环的前10个图像中。

没有错误消息,它只是跳过我的一些代码,出于某种原因...我的断点在parralel时不起作用,因此很难调试。

有关如何进行/调试/修复的任何建议?

所请求的代码

    private static void GenerateIcons(Effects effect)
    {
        DirectoryInfo dir = new DirectoryInfo(HttpContext.Current.Server.MapPath(@"~\Icons\Original\"));

        FileInfo[] ff = dir.GetFiles();

        string mappath = HttpContext.Current.Server.MapPath(@"~\Icons\");

        List<string> paths = new List<string>();

        string ids = GetAllEffectIds(effect.TinyUrlCode);

        Parallel.ForEach(ff, item =>
        {
            if (!File.Exists(mappath + @"Generated\" + ids + "-" + item.Name))
            {
                paths.Add(mappath + @"Generated\" + ids + "-" + item.Name);
                ApplyEffects f = new ApplyEffects(effect, item.Name, mappath);
                f.SaveIcon();


            }
        });
        //Zip icons!
        ZipFiles(paths, effect.TinyUrlCode, ids, effect.General.Prefix);

    }

3 个答案:

答案 0 :(得分:1)

我的理论是,由于List<T>不是线程安全的,因此路径列表未正确更新。基本上,如果两个线程尝试将一个项目同时添加到列表中,则可能会发生任何数量的奇怪事情,例如结果列表中缺少4个项目。尝试使用lock statement

Parallel.ForEach(ff, item =>
{
    if (!File.Exists(mappath + @"Generated\" + ids + "-" + item.Name))
    {
        lock(paths) 
        {
            paths.Add(mappath + @"Generated\" + ids + "-" + item.Name);
        }
        ApplyEffects f = new ApplyEffects(effect, item.Name, mappath);
        f.SaveIcon();
    }
});

答案 1 :(得分:1)

您可以使用更具功能性的方式重新编写它,以希望删除线程问题:

private static void GenerateIcons(Effects effect)
{
    var dir     = new DirectoryInfo(HttpContext.Current.Server.MapPath(@"~\Icons\Original\"));
    var mappath = HttpContext.Current.Server.MapPath(@"~\Icons\");
    var ids     = GetAllEffectIds(effect.TinyUrlCode);

    var filesToProcess = dir
        .EnumerateFiles()
        .AsParallel()
        .Select(f => new { info = f, generated = File.Exists(mappath + @"Generated\" + ids + "-" + f.Name) })
        .ToList();

    Parallel.ForEach(filesToProcess.Where(f => !f.generated), file =>
    {
        new ApplyEffects(effect, file.info.Name, mappath).SaveIcon();
    });

    //Zip icons!
    ZipFiles(filesToProcess.Select(f => f.info), effect.TinyUrlCode, ids, effect.General.Prefix);
}

答案 2 :(得分:0)

  1. 您是否使用非并行版本进行了检查?

  2. 你在使用吗? 未标记为线程安全的API函数?

  3. 要回答1),互斥锁定整个功能并测试错误。

    要回答2)减少互斥锁中的代码量,直到找到有问题的功能。你可以通过二分法来做到这一点。

    ConcurrentBag<T>是一个线程安全的容器。