繁体   English   中英

c#从后台线程使用Invoke的线程问题

[英]c# Thread issue using Invoke from a background thread

我有线程,它处理一些分析工作。

   private static void ThreadProc(object obj)
    {
        var grid = (DataGridView)obj;
        foreach (DataGridViewRow row in grid.Rows)
        {
            if (Parser.GetPreparationByClientNameForSynonims(row.Cells["Prep"].Value.ToString()) != null)
                UpdateGridSafe(grid,row.Index,1);
            Thread.Sleep(10);
        }
    }

我想在循环中安全地更新我的 gridView,所以我使用经典方式:

    private delegate void UpdateGridDelegate(DataGridView grid, int rowIdx, int type);
    public static void UpdateGridSafe(DataGridView grid, int rowIdx, int type)
    {
        if (grid.InvokeRequired)
        {
            grid.Invoke(new UpdateGridDelegate(UpdateGridSafe), new object[] { grid, rowIdx, type });
        }
        else
        {
            if (type == 1)
                grid.Rows[rowIdx].Cells["Prep"].Style.ForeColor = Color.Red;
            if (type==2)
                grid.Rows[rowIdx].Cells["Prep"].Style.ForeColor = Color.ForestGreen;

        }
    }

但是当我进入UpdateGridSafe 时,程序就挂了。

在调试器中,我看到 grid.Invoke 没有调用 UpdateGridSafe。 请帮忙 - 怎么了?

编辑

经典线程创建代码

        Thread t = new Thread(new ParameterizedThreadStart(ThreadProc));
        t.Start(dgvSource);
        t.Join();
        MessageBox.Show("Done", "Info");

你有一个僵局。 您的 t.Join 正在阻塞 GUI 线程,直到 ThreadProc 完成。 ThreadProc 被阻塞,等待 t.Join 完成以便它可以执行调用。

错误代码

    Thread t = new Thread(new ParameterizedThreadStart(ThreadProc)); 
    t.Start(dgvSource); 
    t.Join();  <--- DEADLOCK YOUR PROGRAM
    MessageBox.Show("Done", "Info"); 

好代码

   backgroundWorker1.RunWorkerAsync

  private void backgroundWorker1_DoWork(object sender, 
        DoWorkEventArgs e)
    {    
        var grid = (DataGridView)obj;    
        foreach (DataGridViewRow row in grid.Rows)    
        {    
            if (Parser.GetPreparationByClientNameForSynonims(row.Cells["Prep"].Value.ToString()) != null)    
                UpdateGridSafe(grid,row.Index,1);    
            // don't need this Thread.Sleep(10);    
        }    
    }  

   private void backgroundWorker1_RunWorkerCompleted(
            object sender, RunWorkerCompletedEventArgs e)
        {
        MessageBox.Show("Done", "Info"); 
}

编辑

也可以使用 BeginInvoke 而不是 Invoke。 这样您的工作线程就不必在每次更新 GUI 时都阻塞。

参考

避免 Invoke(),更喜欢 BeginInvoke()

这是因为您正在加入您的工作线程。 您的 UI 线程启动后台线程,然后对其调用 Join。 这会阻止 UI 线程执行任何其他操作。

在此期间,后台线程正在执行其工作并调用 Invoke,后者等待 UI 线程响应。 因为 UI 线程正在等待 Join,所以它永远不会处理要调用的请求。 因此,陷入僵局。

您应该做的是消除Join 和MessageBox。 将 MessageBox 放入它自己的函数中。

void NotifyDone() {
    if( InvokeRequired ) BeginInvoke( (MethodInvoker) NotifyDone );
    else {
        // Perform any post-processing work
        MessageBox.Show("Done", "Info");  
    }
}

后台线程完成后,只需调用此方法(并从 ThreadProc 中消除静态)。

private void ThreadProc(object obj)  
    {  
        var grid = (DataGridView)obj;  
        foreach (DataGridViewRow row in grid.Rows)  
        {  
            if (Parser.GetPreparationByClientNameForSynonims(row.Cells["Prep"].Value.ToString()) != null)  
                UpdateGridSafe(grid,row.Index,1);  
            Thread.Sleep(10);  
        }  
        NotifyDone();
    }  

就像其他人已经说过的那样,使用 Sleep,尤其是在如此低的时间间隔内,要么是危险的,要么是误导性的,要么是毫无价值的。 我在算它一文不值的阵营。

Invoke 语句将等到主线程的消息泵不忙,并且可以处理新消息。 如果您的主线程很忙,则调用将挂起。

在您的情况下,您的顶部代码看起来像是在一个紧密循环中运行,因此底部代码中的 Invoke 永远没有机会实际运行。 如果您将上层代码块中的 Thread.Sleep 更改为包含时间的内容,希望这将使您的主线程有机会处理 .Invoke 调用。

根据您的主应用程序线程正在执行的操作,您可能需要在任何 .Invoke 调用运行之前实际完成第一个循环 - 如果是这种情况,我可以发布一些可能会更好地工作的修改后的代码。

永远,永远,每个人都使用 Thread.Sleep(0)。 它不会做你认为它会做的事情,只会给你带来痛苦。 例如,在一个紧密循环中,操作系统可能会决定刚刚休眠的线程是下一个要运行的线程。 因此,您实际上不会产生线程。

每 N 次迭代使用 Thread.Sleep(1) 再次尝试您的代码,其中 N 大约是 0.25 到 1.0 秒的工作价值。

如果这不起作用,请告诉我,我们可以看看 ThreadProc 是如何创建的。

参考

永不休眠(0) 无限循环

编辑

从不使用 Thread.Sleep 的参数

Thread.Sleep 是程序设计不佳的标志。

您也可能在从不同线程同时访问网格时遇到问题。 DataTables 不是线程安全的,所以我猜 DataGridView 也不是。 这是这篇关于 DataRow 和并发的文章中的一些示例代码,您将在其中使用 Monitor.Enter 和 Montori.Exit 来实现一些并发。

    public void DoWorkUpdatingRow(object state)
    {
        List<DataRow> rowsToWorkOn = (List<DataRow>)state;
        foreach (DataRow dr in rowsToWorkOn)
        {
            Monitor.Enter(this);
            try
            {
                dr["value"] = dr["id"] + " new value";
            }
            finally
            {
                Monitor.Exit(this);
            }
        }
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM