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



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

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

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

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

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

请求的代码

    private static void GenerateIcons(Effects effect)
    {
        DirectoryInfo dir = new DirectoryInfo(HttpContext.Current.Server.MapPath(@"~IconsOriginal"));
        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);
    }

您可以以更实用的样式重写它,希望可以消除线程问题:

private static void GenerateIcons(Effects effect)
{
    var dir     = new DirectoryInfo(HttpContext.Current.Server.MapPath(@"~IconsOriginal"));
    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);
}

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

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. 您是否检查过无并行版本?

  2. 您是否使用任何未标记为线程安全的 API 函数?

回答 1),互斥锁整个函数并测试错误。

回答 2) 减少互斥锁中的代码量,直到找到有问题的函数。您可以通过平分来执行此操作。

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

最新更新