【问题标题】:How to constantly check if threads are alive如何不断检查线程是否处于活动状态
【发布时间】:2017-03-16 13:25:59
【问题描述】:

我需要跟踪我创建的所有线程何时完成。在过去的两天里,我搜索并尝试了一个又一个示例,但无济于事,我想知道是否可以得到一些帮助。 提前谢谢你。

我的表单根据用户选中的复选框对按钮单击运行一系列查询。这些复选框中的每一个都有自己的线程,在按钮单击时创建。 Datagridviews 和图表是根据复选框选择填充的。

这是按钮单击和正在创建的两个线程。 我在查询运行时禁用了表单上的控件,因为我不希望用户能够在另一个查询请求正在处理时发送另一个查询请求。

是否有一种方法可以让我不断检查我的线程是否已完成,然后重新启用我的表单控件?

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();
    }
}

这是来自其中一个子线程的一些代码

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 .......";
}

【问题讨论】:

  • 如果您因为线程在数据库上等待而启动线程,您可能会考虑使用 async/await 模式。将任务添加到线程池而不是尝试自己管理它们几乎总是一个更好的主意。看看TPL api

标签: c# multithreading winforms


【解决方案1】:

如果您想在线程完成之前阻止执行,请使用

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

// here i am sure two threads have finished

它将阻止执行直到完成。

更多示例请参见here

【讨论】:

  • 如果您在 UI 线程上运行此代码,那么您是对的。如果你将它推送到一个单独的线程并在之后运行一个关闭例程,那么 UI 线程不会注意到它。
  • 我如何将它传递给单独的线程?上面的代码在按钮单击时运行。我不想冻结 UI,因为随着 datagridviews 和图表加载,我希望用户能够查看数据、导出数据等......只是不要提交另一个查询请求。
  • 在按钮点击中,以TaskFactory.StartNew(() => yourCode());开头
  • 但是你不能输入任务内部的所有逻辑,因为你只能从主线程更新UI。
  • @Spartanias async/await 模式允许您在 ui 线程上完成所有这些操作。确实值得仔细研究。
【解决方案2】:

使用任务。它们更易于使用。

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);
}

【讨论】:

    【解决方案3】:

    今天经过大约 8 小时的研究,我找到了适合我的解决方案。

    在我最终让事情为我工作之前,我尝试了大约 10 种不同的做事方式,这可能看起来很糟糕。 我本可以在 5 小时前找到解决方案,但我没有意识到在执行 while 循环时,您必须调用 Forms.Application.DoEvents(); 才能使 UI 可用。

    在我弄清楚这一点之后,我在尝试评估所有线程的状态时仍然收到错误,我将在下面详细说明。

    创建的方法来处理我的线程开始

        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();
    
        }
    

    以下内容与我最初发布的内容略有不同。我调用该方法,而不是从按钮单击中启动线程。

    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 循环。

            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;
    
    
        }
    

    我将尝试结合 thrd is null 和 thrd is alive 评估,看看我是否不能简化代码的外观。 也许像......

    while((thrd1.IsAlive!=Null && thrd1.Isalive== true) || 
          (thrd3.IsAlive!=Null && thrd3.Isalive== true))
    

    【讨论】:

    • 感谢@viperassassin 和其他人的帮助
    猜你喜欢
    • 2012-01-27
    • 1970-01-01
    • 2020-01-11
    • 2019-12-10
    • 2020-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多