Home > database >  Async tasks inside of task
Async tasks inside of task

Time:02-16

I'm loading hundreds of xml files to two different datatables in order to bind and display each datatable in a grid. This is for a Windows forms application.

Each query is very different from the other, so running one query and loading both tables doesn't work.

I'm using this to load the xml:

    public async Task loadXMLs(List<string> xmlfiles)
    {

        var loadTasks = new List<Task>();

        foreach (String xml in xmlfiles)
        {
            loadTasks.Add(Task.Run(() => openXMLs(xml)));
        }            

        await Task.WhenAll(loadTasks);
        
    }
    
    public static async Task openXMLs(string xml)
    {           
        XDocument xdoc = XDocument.Load(xml);;

        if(xdoc != null)
        {
            loadXMLdatatable1(xdoc, xml);
            loadXMLdatatable2(xdoc, xml);               
        }
    }
    
    public static async Task loadXMLdatatable1(XDocument xdoc, string xml)
    {
        var query = xdoc.Elements (Long query here for XML)
        
        lock(datatable1)
        {
            foreach(var item in query)
            {
                datatable1.Rows.Add(
                    item
                );
            }
        }           
    }
    
    public static async Task loadXMLdatatable2(XDocument xdoc, string xml)
    {
        var query = xdoc.Elements (Long query here for XML)
        
        lock(datatable2)
        {
            foreach(var item in query)
            {
                datatable2.Rows.Add(
                    item
                );
            }
        }           
    }

My question is, when I change to this:

    public static async Task openXMLs(string xml)
    {
        XDocument xdoc = XDocument.Load(xml); ;

        if (xdoc != null)
        {
            Task dt1 = Task.Run(() => loadXMLdatatable1(xdoc, xml));
            Task dt2 = Task.Run(() => loadXMLdatatable2(xdoc, xml));

            await Task.WhenAll(dt1, dt2);
        }
    }

I thought I would see an increase in performance, but the load times are the same.

Am I doing this correctly or is it not possible to run async inside of a task?

CodePudding user response:

Working with data tables is always awkward, because they just don't support modern concepts of asynchronous loading. So there's a certain point at which you'll just need to switch to a different UI, or make other potentially drastic changes like virtualized data.

The best you can do is push as much work as possible outside the lock. In your case, you have a query, but LINQ uses deferred execution, so unless you're reifying your query before the lock, then the actual query is executed inside the lock.

To reify, you can call ToList at the end of your query:

public static async Task loadXMLdatatable1(XDocument xdoc, string xml)
{
  var items = xdoc.Elements()
      .Whatever(Long query here for XML)
      .ToList();

  lock(datatable1)
  {
    foreach(var item in items)
    {
      datatable1.Rows.Add(item);
    }
  }           
}

That way, the query will be executed while not holding the lock.

If that doesn't provide sufficient performance improvement, or if your query already ends with reification, then you'll just need to use data virtualization (possibly with a UI rewrite).

  • Related