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

时间:2021-12-01 13:53:31

I have a Parallel foreach function that creates a new instance of a class, that manipulates a picture, and saves it to the disk...

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

However approximately 4 times out of 400, the picture gets saved to the disk, but without being manipulated, my theory is that when it happens, some of the propperties existing in my class is null, when they are not suppost to...

然而,400次中大约有4次,图片被保存到磁盘上,但是没有被操作,我的理论是,当它发生时,类中存在的一些propperties是空的,当它们不是替换为……

The 4 (sometimes 3) errors mostly occurs in the first 10 images of the parallel loop.

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

There is no error message, it justs skip some of my code, for some reason... My breakpoint doesn't work when it is parralel, so it is hard to debug.

没有错误信息,出于某种原因,它会跳过我的一些代码……我的断点在parralel时不能工作,所以很难调试。

Any advice on how to proceed / debug / fix ?

关于如何进行/调试/修复有什么建议吗?

The code as requested

请求的代码

    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 个解决方案

#1


1  

You can re-write it in a more functional style, to hopefully remove the threading issues:

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

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


1  

My theory is that your list of paths is not being updated properly due to List<T> not being thread safe. Essentially if two threads try and add an item to the list at the same time any number of weird things could happen, like 4 items missing from the resulting list. Try using the lock statement.

我的理论是,由于列表 不是线程安全的,所以没有正确地更新路径列表。本质上,如果两个线程同时尝试向列表中添加一个项,那么任何奇怪的事情都可能发生,比如结果列表中缺少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();
    }
});

#3


0  

  1. Have you checked with the none parallel version?

    你检查过无并行版本吗?

  2. Are you using any API functions that is not marked as thread safe?

    您是否使用了没有标记为线程安全的API函数?

To answer 1), mutex lock the entire function and test for errors.

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

To answer 2) reduce the amount of code in the mutex until you find the offending function. You can do this by bisection.

要回答2)减少互斥对象中的代码量,直到找到出错的函数为止。你可以用二分法。

ConcurrentBag<T> Is a thread safe container.

ConcurrentBag 是一个线程安全容器。

#1


1  

You can re-write it in a more functional style, to hopefully remove the threading issues:

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

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


1  

My theory is that your list of paths is not being updated properly due to List<T> not being thread safe. Essentially if two threads try and add an item to the list at the same time any number of weird things could happen, like 4 items missing from the resulting list. Try using the lock statement.

我的理论是,由于列表 不是线程安全的,所以没有正确地更新路径列表。本质上,如果两个线程同时尝试向列表中添加一个项,那么任何奇怪的事情都可能发生,比如结果列表中缺少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();
    }
});

#3


0  

  1. Have you checked with the none parallel version?

    你检查过无并行版本吗?

  2. Are you using any API functions that is not marked as thread safe?

    您是否使用了没有标记为线程安全的API函数?

To answer 1), mutex lock the entire function and test for errors.

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

To answer 2) reduce the amount of code in the mutex until you find the offending function. You can do this by bisection.

要回答2)减少互斥对象中的代码量,直到找到出错的函数为止。你可以用二分法。

ConcurrentBag<T> Is a thread safe container.

ConcurrentBag 是一个线程安全容器。