Task.WaitAll() not working as expected

2019-02-14 15:49发布

I'm trying to figure out how to work with the Task class. In the past I have always used the regular Thread class, but I'm trying to grasp all of the asynchronous programming...

As an example, I created a main Winforms application that has all the code. The relevant code for my problem is:

//Relevant delegates
public delegate void MethodAction(int num);
public delegate void MethodConversion();
public delegate void OnCompletionAction(string completiontext);

//Button user presses
private void button4_Click(object sender, EventArgs e)
{
    richTextBox1.Clear();
    sw.Reset();
    sw.Start();
    Sync.RunAsync3(calcSim);
}

//The method that simulates a calculation by adding a sleep
//the input param threadlength is just to allow threads to take longer than others
//since I'm multithreading, I have to invoke the writing code on the windows RichTextbox control
private void calcSim(int threadlength)
{
    string threadname = Thread.CurrentThread.Name;
    for (int i = 0; i < 10; i++) //Thread calc should take 3s
    {
        Thread.Sleep(300 + threadlength);
        richTextBox1.Invoke((MethodConversion)(() =>
                            { 
                                richTextBox1.AppendText(string.Format("Thread: {0}\tVersion: {1}\n", threadname, (i + 1).ToString())); 
                            }));
    }
}

//Class that contains the different processing methods
public static class Sync
{
    public static event OnCompletionAction OnProcCompletion;

    public static void RunAsync3(MethodAction doM)
    {
        Task[] t = new Task[4];
        for(int i = 0; i < 4; i++)
        {
            t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
        }
        Task.WaitAll(t);
        if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
    }
}

The problem lies within Task.WaitAll(t)... For some reason, which I can't figure out, it completely blocks on that line and doesn't respond anymore. If I omit that line, the form gets updated in realtime and the execution take about 3 seconds.

My question is: why isn't Task.WaitAll() blocking the UI thread for 3 seconds before releasing it and allowing the rest of the code to execute?

I know it should be blocking the UI for some time (until all threads are calculated), but it blocks the complete app endlessly. It seems to be waiting forever?

EDIT

I've been suggested to use WhenAll instead of WaitAll. I have rewritten RunAsync3 as follows:

public static void RunAsync3(MethodAction doM)
{
    Task[] t = new Task[4];
    for(int i = 0; i < 4; i++)
    {
        t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
    }
    //Task.WaitAll(t); -> deadlock
    Task.WaitAll(new Task [] { Task.WhenAll(t) });
    if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
}

But this is still getting deadlocked...? I might be using the WhenAll incorrectly?

EDIT 2

Because everybody claiming that I was blocking the UI thread were right, I decided to try this another way: by running a new thread as my calling thread inside the UI thread (so that blocking now would occur on my thread instead of UI thread). This works, but is obviously not the best way to do this!

private void button4_Click(object sender, EventArgs e)
{
    Thread t = new Thread(new ThreadStart(() =>
    {
        richTextBox1.Invoke((MethodConversion)(() => richTextBox1.Clear()));
        sw.Reset();
        sw.Start();
        Sync.RunAsync3(calcSim);
    }));
    t.Start();
}
public static void RunAsync3(MethodAction doM)
{
    Task[] t = new Task[4];
    for(int i = 0; i < 4; i++)
    {
        t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
    }
    Task.WaitAll(t);
   //Task.WaitAll(new Task [] { Task.WhenAll(t) });
   if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
}

3条回答
别忘想泡老子
2楼-- · 2019-02-14 16:14

Task.WaitAll blocks and waits for all task to complete and you are calling it on the UI thread.

All your task are trying to call richTextBox1.Invoke (in the UI thread) but your UI thread is blocked in Task.WaitAll. Deadlock.

查看更多
做自己的国王
3楼-- · 2019-02-14 16:17

You're causing a deadlock.

The UI thread is waiting for 4 tasks to be completed.

On the other hand, those 4 tasks, running calcSim are trying to invoke code on the UI thread -> Deadlock.

You should be using Task.WhenAll() instead. That method will return a new task that will be marked as completed when all your for tasks have completed. If you await that task, your UI thread will be freed, and so calcSim will be able to invoke code on the UI thread, avoiding a deadlock.

Update

You're using it wrong. You're still using WaitAll, which is a blocking call. You should replace it with WhenAll.

await Task.WhenAll(t);

From the documentation:

Creates a task that will complete when all of the supplied tasks have completed.

By calling await on the result, your UI thread will be free - until all 4 tasks complete. When that happens, your RunAsync3 method will resume.

查看更多
萌系小妹纸
4楼-- · 2019-02-14 16:21

Because it waits as your threads finish. They run exactly 3 seconds 300X10

查看更多
登录 后发表回答