Home > Software design >  C# How to pass unique string to async method?
C# How to pass unique string to async method?

Time:11-15

I'm creating several threads passing the different data to each. However, in CallAsync method I'm receiving identical data sometimes.

Why the input and output data differs?

How correctly pass the unique string to each thread?

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

namespace NTest
{
    class Program
    {
        static async Task<int> CallAsync(string str)
        {
            Console.WriteLine("income: {0}", str);
            // await ...
            return 0;
        }

        static async void WorkerMainAsync()
        {
            List<Task> trackedTasks = new List<Task>();

            int[] jobs = new int[] { 0,1,2,3,4,5 };

            string str;

            foreach (int num in jobs)
            {
                str = num.ToString();
                Console.WriteLine("pass: {0}", str);
                trackedTasks.Add(Task.Run(() => CallAsync(str)));
            }
            await Task.WhenAll(trackedTasks);

            Console.WriteLine("[*] All jobs finished.");
        }

        static void Main(string[] args)
        {
            WorkerMainAsync();
            Console.ReadLine();
        }
    }
}

Console output:

pass: 0
pass: 1
pass: 2
income: 1
income: 2
pass: 3
pass: 4
income: 4
income: 4
income: 4
pass: 5
income: 5
[*] All jobs finished.

CodePudding user response:

This doesn't have anything to do with the fact that the methods are asynchronous.

This behavior is because lambda expressions close over variables, not values. So the expression () => CallAsync(str) always refers to the same str variable, which ends up getting shared between all the invocations.

One simple fix is to declare the variable inside the loop, so that each lambda closure gets its own variable:

int[] jobs = new int[] { 0,1,2,3,4,5 };

foreach (int num in jobs)
{
  var str = num.ToString();
  Console.WriteLine("pass: {0}", str);
  trackedTasks.Add(Task.Run(() => CallAsync(str)));
}

await Task.WhenAll(trackedTasks);
  • Related