Task.WaitAll freezes UI with Control.Invoke() method.

Parallel tasks are important to complete the independent operation simultaneously. It makes us to complete very heavy and time taking processes in less time when these operations run in multi core environment but we have to follow the rules to make this multi-tasking to work.

Today I experienced such case where my written code put me in a condition of “Dead Lock”. It is situation more than one task want to acquire same resource and one is already holding this resource but another also want to access it. If no task able to complete its operation then it creates dead lock situation.

When we creating application, in some cases it may be possible update the UI in various threads. E.g. Progress reporting. I found a case which let our application to put in to a situation which freeze application. Let there was few tasks which are independent of each other but we need to merge the result all of these tasks with some required condition.

List<Task> tasks = new List<Task>();
//Create and start independent tasks
tasks.Add(Task.Factory.StartNew<List<string>>(() => CreateInitialData()));
tasks.Add(Task.Factory.StartNew<List<string>>(() => CreateValidationData()));
tasks.Add(Task.Factory.StartNew<List<string>>(() => CreateGUILookup()));

Now we need to complete these tasks to get the results, so that we can merge it. To do this, Task Parallel Library has Task.WaitAll method which takes array of task as parameter. It waits until all the operation do not complete. In other words, it hold the UI and wait to complete all the tasks.

Task.WaitAll(tasks.ToArray());
//Merge result of all task on completion

In one of the thread, some sub task is done which make call to the UI. It was working fine for a while until I made some changes. I was trying to get one property of the progress bar and it was accessed in asynchronous way using the “BeginInvoke” method. It was a bug in the application which does not return the correct results so I replace it with “Invoke” method.

“Control.Invoke()” method waits and return the results immediately while BeginInvoke method is asynchronous approach. It leaves control from the current statement without returning the values and we are not able to get returned result until we call AsynResult.EndInvoke().

//At some instance code by mistake try to access 
// the properties of the any control and made dead lock
// 
int progress = (int)this.Invoke((Func<int>)delegate 
{ return progressBar1.Value; });
System.Diagnostics.Debug.Write(progress);

Now what WaitAll does, it blocks until all tasks have finished. The tasks can't finish because Invoke will run its action on the UI thread, which is already blocked by WaitAll. It is creating deadlock because Invoke waits for the UI thread to unblock.

Complete example:

namespace ParallelWaitDeadLock
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            List<Task> tasks = new List<Task>();
            //Create and start indepenet tasks
            tasks.Add(Task.Factory.StartNew<List<string>>(() => CreateInitialData()));
            tasks.Add(Task.Factory.StartNew<List<string>>(() => CreateValidationData()));
            tasks.Add(Task.Factory.StartNew<List<string>>(() => CreateGUILookup()));
 
            Task.WaitAll(tasks.ToArray());
            //Merge result of all task on completion
       
        }
 
        private List<string> CreateInitialData()
        {
            //
            Task.Delay(1000);
            return new List<string>();
        }
 
        private List<string> CreateGUILookup()
        {
            //
            Task.Delay(1000);
            //At some instance code by mistake try to access 
            // the properties of the any control and made dead lock
            // 
            int progress = (int)this.Invoke((Func<int>)delegate 
            { return progressBar1.Value; });
            System.Diagnostics.Debug.Write(progress);
            return new List<string>();
        }
 
        private List<string> CreateValidationData()
        {
            //
            Task.Delay(1000);
            return new List<string>();
        }
    }
}

We need to take care to avoid such situation when sub task try to do UI relation operations even parent task hold the UI and wait to complete the task.