2

I have the following code:

Parallel.ForEach(xRoot.Elements("key"), xKey =>
{
    int id = int.Parse(xKey.Attribute("id").Value);
    string code = xKey.Attribute("code").Value;

    AccountStatus accountStatus = SomeClient.GetAccountStatusAsync(id, code).Result;
);

The count of xRoot.Elements("key") is 3, but ForEach iterates only 2 times. Why?

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
kulaeff
  • 25
  • 3
  • 1
    Are you sure no exceptions are being raised? Did you set a breakpoint at the start of your `ForEach` function and verify it was run 3 times? What is the exact type of `xRoot.Elements("key")`? – Dai Jun 29 '14 at 21:12
  • 1
    Use `await`, not `.Result`. (and `Task.WhenAll(elements.Select())`, not `Parallel.ForEach()`) – SLaks Jun 29 '14 at 21:13
  • To make sure that it is only executing twice, put this declaration before your loop `int loopCounter = 0;` and add `Interlocked.Increment(ref loopCounter);` inside the loop. Check the value after processing has finished to see how many times it executed. – DeanOC Jun 29 '14 at 21:16

2 Answers2

4

Mixing Parallel.ForEach and async/await isn't a good idea. You don't need async methods to execute in parallel, you need them to execute concurrently. Your current code uses threadpool threads to block on an I/O operation, missing the advantage of an async api.

Try this:

var codeIds = xRoot.Elements("key").Select(xKey => new { Id = int.Parse(xKey.Attribute("id").Value, Code = xKey.Attribute("code").Value  });

var codeTasks = codeIds.Select(x => SomeClient.GetAccountStatusAsync(x.Id, x.Code));

await Task.WhenAll(codeTasks);
Community
  • 1
  • 1
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • Great. And what if I need to add a nested loop? For example, `var codeTasks = codeIds.Select(x => SomeClient.GetAccountStatusAsync(x.Id, x.Code).ContinueWith(blabla)); Is it correct? ` – kulaeff Jun 30 '14 at 13:56
  • You *could* do that, but then you would be awaiting on the tasks returned from `ContinueWith` and not on the ones returning from `GetAccountStatusAsync` – Yuval Itzchakov Jun 30 '14 at 15:51
-1

Try:

Parallel.ForEach(xRoot.Elements("key"), async xKey =>
{
   int id = int.Parse(xKey.Attribute("id").Value);
   string code = xKey.Attribute("code").Value;

   AccountStatus accountStatus = await SomeClient.GetAccountStatusAsync(id, code);
);

Or:

Parallel.ForEach(xRoot.Elements("key"), xKey =>
{
   try
   {
      int id = int.Parse(xKey.Attribute("id").Value); // maybe null reference here?
      string code = xKey.Attribute("code").Value; // or here?

      AccountStatus accountStatus = SomeClient.GetAccountStatusAsync(id, code).Result; // or even here?
   }
   catch (Exception ex)
   {
      throw; // add here a breakpoint and check what's up by checking 'ex' object
   }
);
vdrake6
  • 111
  • 1
  • 10