C#处理循环中的大项目列表,如果失败则重试

时间:2014-08-27 05:20:00

标签: c# sql-server foreach parallel-processing

我有一个大型数据库,我使用Entity Framework访问它。我将所有项目都设为List<Item>。当我在循环中处理它们时:

List<Item> lstItems = UnitOfWork.Items.GetAllAsync(); // repository pattern and Unit Of Work patterns
foreach (Item item in lstItems)
{
  //do something with the item
}

我需要的是,如果其中一个项目在循环中处理失败,我希望能够在此循环中忽略它之前重试该操作3次。此外,我不希望循环刹车,因此无论项目处理是成功还是失败,循环都应该继续,直到列表中的最后一项。

我可以用于此目的的模式吗?

我正在考虑使用try catch,如果处理失败,那么在catch部分将其添加到名为List<Item>的新processAgain中,一旦主列表完成处理,然后处理{{1} } list。

5 个答案:

答案 0 :(得分:2)

由于您希望实现并行处理,因此使用Task和异步处理将是一种很好的方法。 因此,您需要定义一个将成为Task / Action的方法或表达式,并在内部包含重试模式:

public const int MAX_RETRY_COUNT = 3;

private void ProcessItemsAsync(Item item, int retryCount)
{        
    // Note: Exceptions thrown here will pop up
    // as AggregateException in Task.WaitAll()
    if (retryCount >= MAX_RETRY_COUNT)
        throw new InvalidOperationException(
            "The maximum amount of retries has been exceeded");

    retryCount++;
    // Either implement try-catch, or use conditional operator.
    try
    {
        // Do stuff with item
    }
    catch(Exception ex)
    {
        // Exception logging relevant? If not, just retry
        ProcessItemsAsync(item, retryCount);
    }
}

定义Task方法后,您可以一次处理大量任务:

public const int BULK_AMOUNT = 10;

private async void ProcessSqlData()
{
    List<Item> lstItems = await UnitOfWork.Items.GetAllAsync();
    for (var i = 0; i < lstItems.Count; i += BULK_AMOUNT)
    {
        // Running the process parallel with to many items
        // might slow down the whole process, so just take a bulk
        var bulk = lstItems.Skip(i).Take(BULK_AMOUNT).ToArray();
        var tasks = new Task[bulk.Length];
        for (var j = 0; j <= bulk.Length; j++)
        {
            // Create and start tasks, use ProcessItemsAsync as Action
            tasks[j] = Task.Factory.StartNew(() => ProcessItemsAsync(bulk[j], 0));
        }
        // Wait for the bulk to complete
        try
        {
            Task.WaitAll(tasks);
        }
        catch (AggregateException e)
        {
            Log.WriteLine(String.Format(
                "The maximum amount of retries has been exceeded in bulk #{0}. Error message: {1}",
                i,
                e.InnerException != null
                    ? e.InnerException.Message
                    : e.Message));
        }
    }
}

但是,如果您知道运行此计算机的计算机具有足够的性能,则可能会增加BULK_AMOUNT。你应该测试它,在这里找到最佳数量。

答案 1 :(得分:0)

您可以使用生成器/消费者模式,如here

在从队列中删除项目之前,只需在Consume方法中创建处理逻辑(重试三次)

答案 2 :(得分:0)

我建议使用递归方法:

    static int errorCounter;

    static void Main(string[] args)
    {
        List<Item> lstItems = new List<Item>();
        foreach (var item in lstItems)
        {
            errorCounter = 0;
            bool succesful = CustomAction(item);
        }
    }

    static bool CustomAction(Item item)
    {
        try
        {
            //your custom actions with the item
        }
        catch (Exception ex)
        {

            if (errorCounter < 3)
            {
                errorCounter++;
                CustomAction(item);
            }
            else
            {
                return false;
            }
        }
        return true;
    }

编辑:如果由于任何原因你不想使用静态计数器,你也可以在Main方法中初始化计数器并将其作为参数传递给CustomAction

答案 3 :(得分:0)

您可以尝试使用递归。这样的事情:

List<Item> lstItems = UnitOfWork.Items.GetAllAsync(); // repository pattern and Unit Of Work patterns
foreach (Item item in lstItems)
{

    YourProcessingFunction(yourArgs, int count);
}

在你的处理函数中(你的Args,int count):

try
{
    if(count<3)
    {
       //Processing
       return someValue;
    }
    else
    {
        return;
    }
}
catch(Exception ex)
{
    return YourProcessingFunction(yourArgs, int count);
}

答案 4 :(得分:0)

不需要递归,因为它会使你的逻辑变得复杂。您可以使用try catch块,如下所示

List<Item> lstItems = UnitOfWork.Items.GetAllAsync(); // repository pattern and Unit Of Work patterns

        foreach (Item item in lstItems)
        {
          try
          {
             //do something with the item
          }
          catch(Exception ex)
          {
             for(int i = 1; i <= 3; i++)
             {
                 // do something with the item
             }
             // to continue external loop
             continue;
          }

    }

我希望它会有所帮助:)。