如何安全地赋予我的功能而不使我的主要形式卡住

Posted

技术标签:

【中文标题】如何安全地赋予我的功能而不使我的主要形式卡住【英文标题】:How to safely give my function without making my main form stuck 【发布时间】:2015-02-11 12:33:05 【问题描述】:

我已经制作了一个 WPF 应用程序,它有一个按钮来移动一些附加的文件 从一列到另一列的列单元格。我按下的那一刻 该按钮显示了一个漂亮的动画并将所有文件移动到下一列的单元格。

但我真正的问题是一旦我给我的函数 color_check(), 我的应用程序卡住了。我真的不知道为什么。有没有 我能得到什么帮助吗?

代码:

 private void button3_Click(object sender, EventArgs e)
    
        Hide();
        bool done = false;
        ThreadPool.QueueUserWorkItem((x) =>
        
            using (var splashForm = new Form4())
            
                splashForm.Show();
                while (!done)

                    Application.DoEvents();
                splashForm.Close();
            
        );

        move(); //file moving function
        //color_check();  if i give this fn, my form stucks and comes to live after 10 - 20 sec
        done = true;
        MessageBox.Show("TEST FINISHED");
        Show();
    

 public void color_check()  //this is my problem making fn
    
        dataGridView1.Refresh();
         string strVal = ini.ReadValue("Action", "Doc-Controller");

        bool authenticated = true;

        if (authenticated == UserInCustomRole(strVal))
        
            foreach (DataGridViewRow row in dataGridView1.Rows)
            
                // Application.DoEvents(); 
                string fName1 = System.IO.Path.GetFileNameWithoutExtension(row.Cells[3].Value.ToString());
                string fName2 = System.IO.Path.GetFileNameWithoutExtension(row.Cells[4].Value.ToString());
                if (!string.IsNullOrEmpty(fName1) && !string.IsNullOrEmpty(fName2))
                
                    var f1 = GetValue(fName1.ToCharArray()[fName1.Length - 2]) * 16 + GetValue(fName1.ToCharArray()[fName1.Length - 1]);
                    var f2 = GetValue(fName2.ToCharArray()[fName2.Length - 2]) * 16 + GetValue(fName2.ToCharArray()[fName2.Length - 1]);
                    //if (System.IO.Path.GetFileName(fName1) != System.IO.Path.GetFileName(fName2))
                    if (f1 > f2)
                    
                        //MessageBox.Show(fName1);
                        DataGridViewCellStyle style = new DataGridViewCellStyle();
                        style.BackColor = Color.Yellow;
                        row.Cells[3].Style = style;
                    
                    else if (f2 > f1)
                    
                        //MessageBox.Show(fName1);
                        DataGridViewCellStyle style = new DataGridViewCellStyle();
                        style.BackColor = Color.Yellow;
                        row.Cells[4].Style = style;
                    

                    if (f1 == f2)
                    
                        DataGridViewCellStyle style = new DataGridViewCellStyle();
                        style.BackColor = Color.Plum;
                        row.Cells[4].Style = style;
                        row.Cells[3].Style = style;
                    
                
            

    

【问题讨论】:

你试过异步吗? @Patrick 那是什么?? 当你必须使用 GUI 时,你通常会卡住你的程序,因为 UI 线程正在处理其他东西,所以你必须使一些方法异步以使这些操作解放 GUI 线程看看linklink color_check() 方法使用BackgroundWorker 线程? 尝试或将您的 color_check 委托给一个任务并运行它或使其异步,但请记住,如果您必须更改 GUI 中的某些内容,则必须调用您想要更改的控件跨度> 【参考方案1】:

问题是您在 button3_click() 上调用的代码阻塞了 UI 线程。这就是为什么它似乎冻结了一段时间 - 代码正在执行,一旦完成,UI 线程就会再次响应。

解决此问题的方法是在另一个线程上异步执行您的操作。在 .NET 4 及更高版本中,您可以使用 Tasks 和 async/await 关键字来帮助您管理它。如果您正在使用早于 .NET 4 的版本,那么您需要查看 BackgroundWorker 或与您的 .NET 版本兼容的其他线程选项。

请注意,如果您想在异步方法中修改 GUI,您可能需要使用 Dispatcher.Invoke() 来安全地进行。

这里有一些链接可以帮助您了解一些可用的方法

C# Blog on Understanding a simple async program

MSDN reference for async/await

Related *** question on how to use BackgroundWorkers

Related *** question on how to access the UI thread directly

【讨论】:

现在当我将我的 color_check() 函数更改为 private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) 时,它工作正常.. 一切都很好.. 但它需要时间.. 完成整个工作/。但是处理速度太慢了..任何提高速度的方法。【参考方案2】:

在普通的 UI 应用程序中,不会同时运行两个在 GUI 上工作的函数。否则会导致很多问题,通常会导致程序崩溃。例如,可能有 2 个函数同时运行,每个函数检查同一个列表是否至少有一个元素,然后删除一个元素 - 因为它们同时运行,它们首先都检查列表是否有 1 个元素。

这就是为什么所有 GUI 函数都在同一个线程中运行,这意味着它们一次只运行一个。 color_check 运行时,其他函数不运行。

您可以启动额外的线程并处理并行执行的工作,您可以加速color_check 函数,例如通过将其分解为一次以较低优先级运行的部分,使用调度员

拿着这个:

public void color_check()  //this is my problem making fn

    dataGridView1.Refresh();
    string strVal = ini.ReadValue("Action", "Doc-Controller");

    bool authenticated = true;

    if (authenticated == UserInCustomRole(strVal))
    
        foreach (DataGridViewRow row in dataGridView1.Rows)
        
           ProcessRow(row);
        
    

并将其更改为:

public void color_check()  //this is my problem making fn

    dataGridView1.Refresh();
    string strVal = ini.ReadValue("Action", "Doc-Controller");

    bool authenticated = true;

    if (authenticated == UserInCustomRole(strVal))
    
        foreach (DataGridViewRow row in dataGridView1.Rows)
        
           Dispatcher.BeginInvoke(DispatcherPriority.Background, ()=>Process(row););
        
    

在这段代码中,Dispatcher.BeginInvoke 告诉 UI 线程它应该在找到时间后立即运行 Process(row)。这可能会导致 200 个 Process(row) 调用等待执行。它仍然全部在 UI 线程上执行,并且一次只执行一件事。如果在前一百个完成后发生鼠标单击,GUI 线程将首先完成第一个和一个,然后处理鼠标单击,然后再处理剩余的调用。

这种方法有一个缺点。通过允许在对Process(row) 的不同调用之间执行其他函数,您可能会得到令人惊讶的结果。特别是如果这些其他过程也改变了单元格样式。

【讨论】:

,,ProcessRow(row); 是什么意思!!!!。我的fn中没有任何谎言。同样,当我给出这个 fn 时,它在 DispatcherDispatcherPriorityProcess 中显示错误。请告诉我。。 这是一种抽象。重构您的代码,以便您在 for 循环中执行的所有操作都由函数完成,并且您当前的代码看起来与我给出的第一个代码示例完全相同。 当我将 color_check() 更改为 private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) 时,它工作正常.. 但处理速度太慢.. 任何提高速度的方法。

以上是关于如何安全地赋予我的功能而不使我的主要形式卡住的主要内容,如果未能解决你的问题,请参考以下文章

如何优雅地测量一只猫的体积,而不使其感到惊恐或受到伤害?

如何使我的功能通用

如何使用大于或等于编程 iOS AutoLayout 约束而不使布局模糊?

如何使我的图像停留在一页上而不重复?

如何在 Xcode 的 Storyboard 中保持高分辨率图像而不使其像素化?

如何在进行选择之前使我的代码中的复选框默认为未选中,同时保持我的功能相同?