1

Inside a loop I am creating a task using Task.Run(//...). Each task contains a WebRequest. At Task.WaitAll Result of one task getting overridden by Result of another Task. Whats wrong I am doing ? When tried with debugger it works fine. Is it because of concurrency ? How to resolve this ? Below is my code snippet:

SomeMethod()
{
   //someItemList.Count() == 5
   int r = 0;
   Task<MyModel>[] myTaskList= new Task<MyModel>[5];
   foreach(var item in someItemList){
       Task<MyModel> t = Task<MyModel>.Run(() => { return 
           SomeOperationWithWebRequest(item); });
       myTaskList[r] = t;
       r++;
   }
   Task.WaitAll(myTaskList); //myTaskList[0].Result...myTaskList[4].Result all are having same output.

  
}

MyModel SomeOperationwithWebRequest(Item){
    
            string URL = "SomeURLFromItem";
            string DATA = "DATAfromItem"
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
            request.Method = "POST";
            request.ContentType = "application/json";
            request.ContentLength = DATA.Length;
            using (Stream webStream = request.GetRequestStream())
            using (StreamWriter requestWriter = new StreamWriter(webStream, System.Text.Encoding.ASCII))
            {
                requestWriter.Write(DATA);
            }
            try
            {
                WebResponse webResponse = request.GetResponseAsync();
                using (Stream webStream = webResponse.GetResponseStream() ?? Stream.Null)
                using (StreamReader responseReader = new StreamReader(webStream))
                {
                  //response
                }

            catch (Exception ex)
            {

            }
            return new MyModel() { // properties
                };
}
Fildor
  • 14,510
  • 4
  • 35
  • 67
CoronaVirus
  • 401
  • 2
  • 7
  • 20
  • 1
    Which version of C#? – Lasse V. Karlsen Oct 08 '20 at 08:55
  • how is SomeOperationWithWebRequest impelemted? Does it use any instance variables that might cause problems? – Ackdari Oct 08 '20 at 08:59
  • Try making a List> and add them with .Add(t). Hsa probably something to do with references being passed around. – lukger Oct 08 '20 at 08:59
  • Too few details on the implementation to detect the root cause. Show SomeOperationWithWebRequest code – Liquid Core Oct 08 '20 at 09:01
  • 2
    I think it's this capture-thing with `item`. I'll find a link ... give me a sec. – Fildor Oct 08 '20 at 09:03
  • 2
    Does it answer your question [Run same code multiple times in parallel with different parameter](https://stackoverflow.com/questions/61343062/run-same-code-multiple-times-in-parallel-with-different-parameter)? It seems, that you've faced with variable loop capture. But starting from C# 5 `foreach` loop doesn't do that – Pavel Anikhouski Oct 08 '20 at 09:05
  • @LasseV.Karlsen c# 5.0 – CoronaVirus Oct 08 '20 at 09:11
  • [How to capture a variable in C# and not to shoot yourself in the foot](https://medium.com/@unicorn_dev/how-to-capture-a-variable-in-c-and-not-to-shoot-yourself-in-the-foot-d169aa161aa6) -> Which says this should have been fixed for `foreach` in C# >= 5.0 – Fildor Oct 08 '20 at 09:13
  • @Fildor - Thanks much. Tried with link given. declared a local variable index and for loop. but same output :( but one thing I observed it is happening randomly not always same. seems like there is some race condition among responses which is overriding all other results. – CoronaVirus Oct 08 '20 at 09:41
  • @LiquidCore - Added SomeOperation method. – CoronaVirus Oct 08 '20 at 09:42
  • Exactly. Since the capture should be fixed for "foreach" in C# 5, it is likely something else. You just double-checked this. So, there is still something hidden... – Fildor Oct 08 '20 at 09:43
  • Just declared a local variable for item inside for loop and it worked. Thanks :) – CoronaVirus Oct 09 '20 at 08:04

1 Answers1

0

I think it works in debugging because you dont await the async WebRequest. Try this:

private readonly List<string> _someItemList = new List<string> { "t1", "t2", "t3" };

private async Task SomeMethodAsync()
{
    var myTaskList = new List<Task<MyModel>>();
    int counter = 0;
    foreach (var item in _someItemList)
    {
        var t = Task.Run(() => SomeOperationWithWebRequestAsync(counter++));
        myTaskList.Add(t);
    }
    await Task.WhenAll(myTaskList);
}

public async Task<MyModel> SomeOperationWithWebRequestAsync(int counter)
{
   //do your async request, for simplicity I just do a delay
    await Task.Delay(counter * 1000);
    return new MyModel {Counter = counter };
}

public class MyModel
{
    public int Counter { get; set; }
}

and use it like:

 await SomeMethodAsync();

Also notice Task.WhenAll vs Task.WaitAll see e.g WaitAll vs WhenAll

less
  • 699
  • 6
  • 25