简体   繁体   English

C# 中带有重试逻辑的 Parallel.ForEach 循环

[英]Parallel.ForEach Loop with Retry Logic in C#

I am using Parallel.ForEach to download multiple files in C# from google bucket to folder location.我正在使用Parallel.ForEach将 C# 中的多个文件从谷歌存储桶下载到文件夹位置。 I'm using retry logic so it can retry downloading files in case files download fails during downloading.我正在使用重试逻辑,因此它可以重试下载文件,以防下载过程中文件下载失败。 How can I apply retry logic for each file or each thread in Parallel.ForEach loop.如何为Parallel.ForEach循环中的每个文件或每个线程应用重试逻辑。

Parallel.ForEach(listFiles, objectName =>            
{
    retryCount = 0;                        
    countOfFiles++;
    downloadSuccess = false;
    bucketFileName = Path.GetFileName(objectName.Name);
    guidFolderPath = tempFolderLocation + "\\" + bucketFileName;

    while (retryCount < retryCountInput && downloadSuccess == false)
    {
        try
        {
            FileStream fs = new FileStream(guidFolderPath, FileMode.Create, FileAccess.Write, FileShare.Write);
            using (fs)
            {                                               
                storage.DownloadObjectAsync(bucketName, objectName.Name, fs, option, cancellationToken, progress).Wait();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception occured while downloading file: " + ex.ToString());                   
            Thread.Sleep(RetryInterval(retryCount, minBackoffTimeSpan, maxBackoffTimeSpan, deltaBackoffTimeSpan));
            retryCount++;

        }
    }
}

I would change it to tasks and use the async.我会将其更改为任务并使用异步。 This way your Thread.Sleep doesn't block a threadpool thread.这样你的 Thread.Sleep 不会阻塞线程池线程。 The Parallel.ForEach is for CPU bound work. Parallel.ForEach 用于 CPU 密集型工作。

Something like: (I'm unable to compile/test this without the rest of your code)类似于:(如果没有您的代码的 rest,我将无法编译/测试它)

int retryCountInput = 5;
var tasks = new List<Task>();

foreach (var file in listFiles)
{
    var task = Task.Run(async () =>
    {
        // make it local
        int retryCount = 0;
        string bucketFileName = Path.GetFileName(objectName.Name);
        string guidFolderPath = tempFolderLocation + "\\" + bucketFileName;

        while (retryCount < retryCountInput)
        {
            try
            {
                using (var fs = new FileStream(guidFolderPath, FileMode.Create, FileAccess.Write, FileShare.Write))
                    // Use await here, instead of `Wait()` so this threadpool thread
                    // can be used for other tasks.
                    await storage.DownloadObjectAsync(bucketName, objectName.Name, fs, option, cancellationToken, progress);

                break;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception occured while downloading file: " + ex.ToString());

                // Use Task.Delay here, so this thread is 'released'
                await Task.Delay(RetryInterval(retryCount, minBackoffTimeSpan, maxBackoffTimeSpan, deltaBackoffTimeSpan));
                retryCount++;
            }
        }
    });
    tasks.Add(task);
}
await Task.WhenAll(tasks);

I have modified my code and removed Parallel.ForEach instead using foreach loop to iterate through files.我已经修改了我的代码并删除了Parallel.ForEach ,而是使用foreach循环来遍历文件。 But now, I'm not able to find all files in downloaded path, though logs shows all files got downloaded.但是现在,我无法在下载路径中找到所有文件,尽管日志显示所有文件都已下载。 Number of downloaded files in download path changes and this behavior seems random.下载路径中下载文件的数量发生了变化,这种行为似乎是随机的。 Can I use Task.Run for I/O operations?我可以使用Task.Run进行 I/O 操作吗?

var tasks = new List<Task>();
foreach (var objectName in listFiles)
{
    var task = Task.Run(() =>
    {
        downloadSuccess = false;
        bucketFileName = Path.GetFileName(objectName.Name);
        guidFolderPath = tempFolderLocation + "\\" + bucketFileName;

        var maxRetryAttempts = 3;
        var pauseBetweenFailures = TimeSpan.FromSeconds(2);
        RetryHelper.RetryOnException(maxRetryAttempts, pauseBetweenFailures, async () =>
        {
            FileStream fs = new FileStream(guidFolderPath, FileMode.Create,
                FileAccess.Write, FileShare.Write);
            using (fs)
            {
                var progress = new Progress<IDownloadProgress>(
                    p =>
                    {
                        DownloadProgress(p, retryCount, objectName.Name);
                    });

                await client.DownloadObjectAsync(bucketName, objectName.Name,
                    fs, option, cancellationToken.Token, progress);
            }
        });
    });
    tasks.Add(task);
}
await Task.WhenAll(tasks);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM