0

I am in need to track when all threads I have created have finished. I have searched and tried example after example over the past two days to no avail and was wondering if I could get some assistance. Thank you in advance.

My form runs a series of queries on button click based off of what check boxes the user checks. Each of these check boxes has it's own thread that gets created on the button click. Datagridviews and graphs are populated based on the checkbox selections.

Here's the button click and two of the threads being created. I disabled the controls on the form while the queries run because I didn't want the user to be able to send another query request while the other was processing.

Is there a method I could make that would constantly check that my threads have completed and would then re-enable my form controls?

public void btn_runGRM_Click(object sender, EventArgs e)
{
    this.btn_runGRM.Enabled = false;
    this.checkBox_Assgn.Enabled = false;
    this.checkBox_Avail.Enabled = false;
    this.checkBox_RFI.Enabled = false;
    this.checkBox_Census.Enabled = false;
    this.ChartDGV.Visible = true;

    if (checkBox_Assgn.Checked)
    {
        AssignmentDGV.DataSource = null;
        AssignmentDGV.Rows.Clear();
        AssignmentDGV.Refresh();

        Assgn_timer = new System.Windows.Forms.Timer();
        Assgn_timer.Interval = (1000);
        Assgn_timer.Tick += new EventHandler(Assgn_timer_Tick);
        Assgn_sw = new System.Diagnostics.Stopwatch();
        Assgn_timer.Start();
        Assgn_sw.Start();

        ThreadStart childref1 = new ThreadStart(CallToChildthread1);
        Thread childThread1 = new Thread(childref1);

        childThread1.Start();
    }
    if (checkBox_Census.Checked)
    {
        CensusDGV.DataSource = null;
        CensusDGV.Rows.Clear();
        CensusDGV.Refresh();

        Census_timer = new System.Windows.Forms.Timer();
        Census_timer.Interval = (1000);
        Census_timer.Tick += new EventHandler(Census_timer_Tick);
        Census_sw = new System.Diagnostics.Stopwatch();
        Census_timer.Start();
        Census_sw.Start();

        ThreadStart childref2 = new ThreadStart(CallToChildthread2);
        Thread childThread2 = new Thread(childref2);

        childThread2.Start();
    }
}

Here is a bit of code from one of the child threads

public void CallToChildthread1()
{
    string oradb = "......";
    OracleConnection conn = new OracleConnection(oradb);  // C#
    conn.Open();
    OracleParameter parm = new OracleParameter();
    parm.OracleDbType = OracleDbType.Varchar2;

    if (textBox1.Text == "")
    {
        parm.Value = ' ';
    }
    else
    {
        parm.Value = textBox1.Text;
    }

    cmd = new OracleCommand();
    cmd.Connection = conn;
    cmd.Parameters.Add(parm);
    cmd.CommandText = "SELECT .......";
}
H H
  • 263,252
  • 30
  • 330
  • 514
Spartanias
  • 31
  • 5
  • 2
    If your spinning up threads because they are waiting on the db you might consider the async/await pattern instead. It's also almost always a better idea to add tasks to the thread pool rather then try to manage them yourself. Take a look at the [TPL api](https://msdn.microsoft.com/en-us/library/dd460717.aspx) . – asawyer Mar 16 '17 at 13:37

3 Answers3

0

If you want to block execution until your threads have finished, use

Thread childThread1 = new Thread(childref1);
...
childThread1.Join();
childThread2.Join();

// here i am sure two threads have finished

it will block execution until it finished.

see here for more example.

Community
  • 1
  • 1
Ygalbel
  • 5,214
  • 1
  • 24
  • 32
  • If you run this code on the UI thread, then you are right. If you push this to a separate thread and run afterward a close up routine it wouldn't be noticeable to the UI thread. – vipersassassin Mar 16 '17 at 14:19
  • How would i pass this to a separate thread? The above code runs on button click. I don't want to freeze the UI because as the datagridviews and chart loads, i want the user to be able to look at the data, export it ect.... just not submit another query request. – Spartanias Mar 16 '17 at 14:28
  • In button click, start with `TaskFactory.StartNew(() => yourCode());` – Ygalbel Mar 16 '17 at 14:30
  • But you can't enter all the logic inside the task, because you can update the UI only from main thread. – Ygalbel Mar 16 '17 at 14:31
  • I feel like I am close, @vipersassassin. I likely need to do a while(childThread1.IsAlive)... My issue is error "childThread1" does not exist in the current context. How do I instantiate (i think is the right word) it? private Thread checkThread; public void CheckThreadComplete() { MessageBox.Show(childThread1.IsAlive.ToString()); } public void CallToChildthread1() { checkThread = new Thread(new ThreadStart(CheckThreadComplete)); checkThread.Start(); – Spartanias Mar 16 '17 at 14:45
  • @Spartanias I think if you will be running down this path and are simply finding an issue with getting the code to properly run outside of the UI thread, you may want to either update your question to reflect this, or ask a new question specific to entering a separate thread. – vipersassassin Mar 16 '17 at 14:52
  • 1
    @Spartanias async/await pattern allows you do this all on the ui thread. It really is worth taking a long hard look at. – asawyer Mar 16 '17 at 15:39
0

Use Tasks. They are easier to work with.

private void StartTasksAndWaitForThemToFinish() {
    // Generate and start worker tasks
    Task[] tasks = new [] {
        TaskFactory.StartNew(() => {
            // task 1
        }),
        TaskFactory.StartNew(() => {
            // task 2
        })
    };

    // Wait for them to finish
    Task.WaitAll(tasks);

    // Update UI. You might need to invoke to the UI thread.
    ...
}

private void SomeButtonClickHandler(object sender, EventArgs e) {
    // Start tasks and wait for them to finish
    TaskFactory.StartNew(StartTasksAndWaitForThemToFinish);
}
Maarten
  • 22,527
  • 3
  • 47
  • 68
0

So after about 8 hours more research today, I have a solution that works for me.

It might be botched looking as I played around with about 10 different ways of doing things before i finally got things working for me. I could have had a solution 5 hours ago but I didn't realize that when doing a while loop, you have to call the Forms.Application.DoEvents(); in order for the UI to be usable.

After I figured that out, i was still receiving errors when trying to assess the state of all threads which I will detail below.

Created Methods to handle my thread starts

    public Thread thrd1;
    public Thread thrd3;

    public void MakeThread1(ThreadStart name)
    {
        thrd1 = new Thread(name);
        thrd1.Start();            
    }
    public void MakeThread3(ThreadStart name)
    {
        thrd3 = new Thread(name);
        thrd3.Start();

    }

The below only differs slightly from what I originally posted. I call the method instead of starting the thread from within the button click.

public void btn_runGRM_Click(object sender, EventArgs e)
    {

        this.btn_runGRM.Enabled = false;
        this.checkBox_Assgn.Enabled = false;
        this.checkBox_Avail.Enabled = false;
        this.checkBox_RFI.Enabled = false;
        this.checkBox_Census.Enabled = false;
        this.ChartDGV.Visible = true;

        if (checkBox_Assgn.Checked)
        {
            AssignmentDGV.DataSource = null;
            AssignmentDGV.Rows.Clear();
            AssignmentDGV.Refresh();

            ThreadStart childref1 = new ThreadStart(CallToChildthread1);
            Thread mt1 = new Thread(childref1);
            MakeThread1(childref1);
        }

        if (checkBox_Assgn.Checked || checkBox_RFI.Checked)
        {
            while (chart1.Series.Count > 0) { chart1.Series.RemoveAt(0); }
            chart1.Visible = true;
            ChartDGV.DataSource = null;
            ChartDGV.Rows.Clear();
            ChartDGV.Refresh();

            ThreadStart childref3 = new ThreadStart(CallToChildthread3);
            Thread mt3 = new Thread(childref3);
            MakeThread3(childref3);
        }

While still inside the button click, I now look to see if the thread is not null. It throws an error if it is. If it isn't, I start my while loop.

        if(thrd1 != null)
        {
            while (thrd1.IsAlive == true)
                try
                {
                    Thread.Sleep(50);
                    System.Windows.Forms.Application.DoEvents();
                }
                catch
                {

                }
        }
        if (thrd3 != null)
        {
            while (thrd3.IsAlive == true)
                try
                {
                    Thread.Sleep(50);
                    System.Windows.Forms.Application.DoEvents();
                }
                catch
                {

                }
        }

        this.btn_runGRM.Enabled = true;
        this.checkBox_Assgn.Enabled = true;
        this.checkBox_Avail.Enabled = true;
        this.checkBox_RFI.Enabled = true;
        this.checkBox_Census.Enabled = true;


    }

I am going to try and combine the thrd is null and thrd is alive evaluations to see if I cant simplify the look of the code. maybe something like...

while((thrd1.IsAlive!=Null && thrd1.Isalive== true) || 
      (thrd3.IsAlive!=Null && thrd3.Isalive== true))
Spartanias
  • 31
  • 5