Search code examples
c#multithreadingblocking

c# Update label from sub-thread when using thread.join on the main thread


I'm stuck trying to do something that seems like it should be easier than I'm making it.

I would like to update a status label (I.E. progress) on the GUI of the program from some worker threads.

I can't use a synchronous invoke call because the main thread is blocked on thread.Join(); to re-enable interface input/cleanup threads. This causes freezing in the program.

I do not want to use an asynchronous BeginInvoke because then it waits until the program is finished to update the progress...defeating the purpose.

tl;dr - I know WHY it won't work, but I'm not sure how to fix it.

Button_Search_Click(object sender, EventArgs e)()
{
    DisableInput(); //Block input on start of function
    Cursor.Current = Cursors.WaitCursor; //set wait cursor

    //read combobox input
    string objectType = comboBox_Object.SelectedItem.ToString();
    string conditionType = comboBox_ConditionType.SelectedItem.ToString();
    string conditionOperator = comboBox_equals.SelectedItem.ToString();

    //create a list of worker threads
    List<Thread> workerThreads = new List<Thread>();

    //for each line in the textbox
    foreach (string searchText in textBox_SearchText.Lines)
    {
        if (String.IsNullOrWhiteSpace(searchText)) continue;

         //foreach line in a listbox
        foreach (String uri in Get_selected_sites())
        {
            string cred = (creddict[uri]);

            Thread thread = new Thread(() => Search(uri, cred, objectType, conditionType, conditionOperator, searchText));
            workerThreads.Add(thread);
            thread.Start(); //start main "work" function
        }

        // Wait for all the threads to finish
        foreach (Thread thread in workerThreads)
        {
            thread.Join();
        }
    }

    Displaydata();
    EnableInput(); //unlock input
    Cursor.Current = Cursors.Default; //set normal cursor
}

Search(uri, cred, objectType, conditionType, conditionOperator, searchText)
{
DoStuff();

//Update toolStripStatusLabel with progress stored in global int variable

//This doesn't work well because it waits until Thread.join() to update status label.
//BeginInvoke(new Action(() => Results_Count.Text = "Total Results = " + totalResults)); 

//This doesn't work at all because the main thread is blocked on Thread.Join()  -- it freezes the program
//statusStrip1.Invoke(new Action(() => Results_Count.Text = "Total Results = " + totalResults)); 

//This doesn't work/errors/crashes because the GUI is being modified by a thread other than main
//Results_Count.Text = "Total Results = " + totalResults;

DoMoreStuff();
}

Thank you very much for any tips you can provide!


Solution

  • Fix your method so it looks more like this:

    async void Button_Search_Click(object sender, EventArgs e)()
    {
        DisableInput(); //Block input on start of function
        Cursor.Current = Cursors.WaitCursor; //set wait cursor
    
        //read combobox input
        string objectType = comboBox_Object.SelectedItem.ToString();
        string conditionType = comboBox_ConditionType.SelectedItem.ToString();
        string conditionOperator = comboBox_equals.SelectedItem.ToString();
    
        //create a list of worker threads
        List<Task> workerTasks = new List<Task>();
    
        //for each line in the textbox
        foreach (string searchText in textBox_SearchText.Lines)
        {
            if (String.IsNullOrWhiteSpace(searchText)) continue;
    
             //foreach line in a listbox
            foreach (String uri in Get_selected_sites())
            {
                string cred = (creddict[uri]);
    
                workerTasks.Add(Task.Run(() => Search(uri, cred, objectType, conditionType, conditionOperator, searchText)));
            }
    
            await Task.WhenAll(workerTasks);
        }
    
        Displaydata();
        EnableInput(); //unlock input
        Cursor.Current = Cursors.Default; //set normal cursor
    }
    

    That way, the Button_Search_Click() won't block the UI thread while the tasks are running, and you can use normal Invoke() or whatever mechanism you like to post updates to the UI thread.