Search code examples
c#lambdapolly

How can I provide a variable value to a lambda expression exactly once?


I'm trying to use the Polly library to monitor/restart tasks that fail. However, I'm having a difficult time keeping the same input arguments between retry attempts.

using System;
using System.IO;
using System.Collections.Generic;
using Polly;
using System.Threading.Tasks;

namespace Sampler
{
    class Program
    {
        public static async Task Main(string[] args)
        {

            // create and execute policies for each sampler
            List<Task> policy_list = new List<Task>();
            for(int i = 0; i < 2; i++)
            {
                var policy = Policy
                    .Handle<Exception>()
                    .RetryForeverAsync()
                    .ExecuteAsync(async () => await Program.TaskMethod(i.ToString()));

                policy_list.Add(policy);
            }

            await Task.WhenAll(policy_list.ToArray());

            Console.WriteLine("Press any key to exit...");
            Console.ReadLine();
        }

        public static async Task TaskMethod(string task_id)
        {
            Console.WriteLine("Starting Task {0}", task_id);
            while (true)
            {
                await Task.Delay(5000);
                Console.WriteLine("Hello from task {0}", task_id);

                int i = 0;
                int b = 32 / i;

            }
        }
    }
}

When I run this code, I get the following output:

Starting Task 0
Starting Task 1
Hello from task 1
Hello from task 0
Starting Task 2 // BAD! this should be starting task 0
Starting Task 2 // BAD! this should be starting task 1
Hello from task 2
Hello from task 2
Starting Task 2
Starting Task 2

The obvious issue is associated with this line:.ExecuteAsync(async () => await Program.TaskMethod(i.ToString())); I am providing the task number as a variable which is a problem because this variable goes to "2" at the end of the for loop. Thus task ID 2 is used for the remainder of retries.

So my question: How would I reformat my lambda expression such that it only references the 'i' variable once such that it maintains its original task ID?


Solution

  • this is called closure. Safe i in a temporary local variable:

    for(int i = 0; i < 2; i++)
    {
        int temp = i;
        var policy = Policy
            .Handle<Exception>()
            .RetryForeverAsync()
            .ExecuteAsync(async () => await Program.TaskMethod(temp.ToString()));
    
        policy_list.Add(policy);
    }
    

    Here is a nice article that explains it far better then I ever could in here.