0

I thought that I got threads in .NET, but when I have added LINQ expression it made me a little confused.

Like I wrote in the topic of this discussion I dont why the thread doesnt return control to the main action of my controller. I have written what makes me silly in comments, so let me skip to the true example:

public class ValuesController : ApiController
    {
        public async Task<List<SomeProduct>> Get()
        {
            var collection = new List<Mother>() {
                new Mother()
                {
                    internalField = new List<Child>()
                    {
                        new Child()
                        {
                            theLastOne = "VAL"
                        },
                        new Child()
                        {
                            theLastOne = "VAL"
                        }
                    }
                }
            };
            var oss =
                from m in collection
                from s in m.internalField
                select Convert(m, s).Result;
            //1-The above code doesnt enter into CONVERT function (I have a breakpoint there)

            return oss.ToList();//2- this list enter into COnvertt
        }

        private async Task<SomeProduct> Convert(Mother ms, Child ss)
        {
            var ossNEW = new SomeProduct();
            await update(ossNEW, ms);
            return ossNEW;
        }

        private async Task update(SomeProduct oss, Mother ms)
        {//3 - Naturally it comes here
            await Task.Run(()=>
            {
                //This task is executed (It is example code, pls do not care, that threads do not have any sense
                oss.copyOfTheLastOne = ms.internalField.First().theLastOne;
                oss.valeFromAnUpdateFunction = "works"; 
            }); //Flow comes here and THIS line does not return control to the main action, why? :)
        }
    }

    public class SomeProduct
    {
        public string copyOfTheLastOne;
        public string valeFromAnUpdateFunction;

    }
    public class Mother
    {
        public List<Child> internalField;

    }

    public class Child
    {
        public string theLastOne;
    }

I have solved this example by adding an "executor", which takes list of the tasks and manage it.

public class ValuesController : ApiController
    {
        public async Task<List<SomeProduct>> Get()
        {
            var collection = new List<Mother>() {
                new Mother()
                {
                    internalField = new List<Child>()
                    {
                        new Child()
                        {
                            theLastOne = "VAL"
                        },
                        new Child()
                        {
                            theLastOne = "VAL"
                        }
                    }
                }
            };
            var oss =
                from m in collection
                from s in m.internalField
                select Convert(m, s);

            List<Task<SomeProduct>> downloadTasks = oss.ToList();
            List<SomeProduct> ossNew = new List<SomeProduct>();

            while (downloadTasks.Count > 0)
            {
                var firstFinishedTask = await Task.WhenAny(downloadTasks);

                downloadTasks.Remove(firstFinishedTask);
                ossNew.Add(await firstFinishedTask);
            }

            return ossNew;
        }

        private async Task<SomeProduct> Convert(Mother ms, Child ss)
        {
            var ossNEW = new SomeProduct();
            await update(ossNEW, ms);
            return ossNEW;
        }

        private async Task update(SomeProduct oss, Mother ms)
        {
            await Task.Run(()=>
            {
                oss.copyOfTheLastOne = ms.internalField.First().theLastOne;
                oss.valeFromAnUpdateFunction = "works"; 
            }); 
        }

To fully understand the problem, I would like to know why the UPDATE function does not return control to the main action and why RESULT on CONVERT function does not force to run program synchronously?

Lukasz
  • 13
  • 4

1 Answers1

1

I would like to know why the UPDATE function does not return control to the main action and why RESULT on CONVERT function does not force to run program synchronously?

You're running into a common deadlock problem that I explain in full on my blog, due to the use of Result. Use await instead of Result and your problem goes away (in your case, since you have a collection, you'll want to await Task.WhenAll):

public async Task<SomeProduct[]> Get()
{
  var collection = new List<Mother>() {
      new Mother()
      {
        internalField = new List<Child>()
        {
          new Child()
          {
            theLastOne = "VAL"
          },
          new Child()
          {
            theLastOne = "VAL"
          }
        }
      }
  };
  var oss =
      from m in collection
      from s in m.internalField
      select Convert(m, s);
  return Task.WhenAll(oss);
}

On a side note, you shouldn't use Task.Run in your implementations, particularly on ASP.NET. On ASP.NET, Task.Run completely removes all the benefits of async and adds overhead.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810