尝试更新datagridview时接口冻结

时间:2016-12-21 20:13:57

标签: c# datagridview

我使用下面的代码复制文件并在datagridview设置状态列以通知用户连接已启动但是当我按下按钮执行时 方法接口冻结...

我搜索了很多,我知道使用task.run();是不可能的,因为它.not 4中没有包含.net 4.5的新功能我也知道Task.Factory.StartNew();可以使用而不是使用task.run(),但它有很多风险作为隐式线程,我知道使用显式线程也很好选择一个

我希望得到一些帮助继续我的项目并继续学习,而不是在那个无聊的点上堆积

public void PatchUpdates()
{
    try
    {
        foreach (DataGridViewRow OfficeListRow in DGV_OfficeList.Rows)
        {
            string OfficeIPAddress = OfficeListRow.Cells[3].Value.ToString();

            foreach (DataGridViewRow FileListRow in DGV_FileList.Rows)
            {
                string SoruceFileNamePath = FileListRow.Cells[4].Value.ToString();

                string DestinationFileNamePath = @"\\" + OfficeIPAddress + @"\usb1_1\test\" + Path.GetFileName(SoruceFileNamePath);

                //check if connection to remote server is available
                var vResult = CheckOffice(OfficeIPAddress);

                if (vResult == 1)
                {
                    DGV_OfficeList[4, DGV_OfficeList.CurrentCell.RowIndex].Value = "Connected";
                    File.Copy(SoruceFileNamePath, DestinationFileNamePath, true); //copy files...
                }
                else if (vResult == 0)
                {
                    DGV_OfficeList[4, DGV_OfficeList.CurrentCell.RowIndex].Value = "disconnected";
                    break;
                }
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

检查下面的办公室代码

    public int CheckOffice(string _ipAddress)
    {
        int timeout = 120;
        string data = "PingTestData";
        byte[] buffer = Encoding.ASCII.GetBytes(data);

        Ping PingSender = new Ping();
        PingOptions options = new PingOptions();

        options.DontFragment = true;

        PingReply reply = PingSender.Send(_ipAddress, timeout, buffer, options);

        if (reply.Status == IPStatus.Success)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }

下面我试图制作线程,但不会解决我的问题

public void PatchUpdates()
{
    try
    {
        foreach (DataGridViewRow OfficeListRow in DGV_OfficeList.Rows)
        {
            string OfficeIPAddress = OfficeListRow.Cells[2].Value.ToString();

            foreach (DataGridViewRow FileListRow in DGV_FileList.Rows)
            {
                string SoruceFileNamePath = FileListRow.Cells[4].Value.ToString();
                string DestinationFileNamePath = @"\\" + OfficeIPAddress + @"\usb1_1\test\" + Path.GetFileName(SoruceFileNamePath);


                Thread foregroundthread = new Thread(() => CheckOffice(OfficeIPAddress));

                foregroundthread.Start();

                //check if connection to remote server is available
                if (CheckOffice(OfficeIPAddress) == 1)
                {
                    DGV_OfficeList[3, DGV_OfficeList.CurrentCell.RowIndex].Value = "Connected";
                    //file.copy(sorucefilenamepath, destinationfilenamepath, true); //copy files...
                }
                else if (CheckOffice(OfficeIPAddress) == 0)
                {
                    DGV_OfficeList[3, DGV_OfficeList.CurrentCell.RowIndex].Value = "disconnected";
                    break;
                }
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

我也尝试了这个,但正如我所说的那样,没有在dot net 4上使用thask.run

    var task = Task.Run(() =>
        {
            var result = CheckOffice(OfficeIPAddress);

            this.BeginInvoke((Action)(() =>
            {
                if (result == 1)
                {
                    DGV_OfficeList[4, DGV_OfficeList.CurrentCell.RowIndex].Value = "Connected";
                    //file.copy(sorucefilenamepath, destinationfilenamepath, true); //copy files...
                }
                else if (result == 0)
                {
                    DGV_OfficeList[4, DGV_OfficeList.CurrentCell.RowIndex].Value = "disconnected";
                }
            }));
        }
    );

-------------------------------------------- -------------更新------------------------------------ ---------------------

    public void PatchUpdates()
    {
        try
        {
            foreach (DataGridViewRow OfficeListRow in DGV_OfficeList.Rows)
            {
                string OfficeIPAddress = OfficeListRow.Cells[3].Value.ToString();
                int RowNum = OfficeListRow.Index;

                foreach (DataGridViewRow FileListRow in DGV_FileList.Rows)
                {
                    string SoruceFileNamePath = FileListRow.Cells[4].Value.ToString();
                    //string DestinationFileNamePath = @"\\" + OfficeIPAddress + @"\usb1_1\test\" + Path.GetFileName(SoruceFileNamePath);
                    string DestinationFileNamePath = @"F:\test\" + Path.GetFileName(SoruceFileNamePath); //TestPurpose

                    Thread t2 = new Thread(new ThreadStart(() =>
                    {
                        int vResult = CheckOffice(OfficeIPAddress);
                        UpdateUI(vResult, RowNum, SoruceFileNamePath, DestinationFileNamePath, OfficeIPAddress);
                    }));
                    t2.Start();
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

更新UI的UpdateUI方法......

    public void UpdateUI(int vResult, int RowNum, string SoruceFileNamePath, string DestinationFileNamePath,string OfficeIPAddress)
    {
        try
        {
            var timeNow = DateTime.Now;

            if ((DateTime.Now - PreviousTime).Milliseconds <= 10)
                return;

            SynchronizationContext.Post(new SendOrPostCallback(o =>
                    {
                        if (vResult == 1)
                        {
                            DGV_OfficeList[4, RowNum].Value = "Connected";
                            //File.Copy(SoruceFileNamePath, DestinationFileNamePath, true);
                            //MessageBox.Show("Pingable " + OfficeIPAddress); //TestPurpose
                        }
                        else if (vResult == 0)
                        {
                            DGV_OfficeList[4, RowNum].Value = "Disconnected";
                            //MessageBox.Show("Not reachable"); //TestPurpose
                        }
                    }), vResult);

            PreviousTime = timeNow;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

enter image description here

enter image description here

1 个答案:

答案 0 :(得分:4)

简短回答:您的代码没有任何问题。设计不好。

长答案

你听到有人说过#34;我一次只能做一件事!&#34;那就是这里发生的事情。您的Windows窗体应用程序的代码正由1个线程执行,该线程一次只能做一件事。在ping时,它会等待回复。如果答复成功,则复制文件。由于你有一个循环,它一直这样做,直到它完成所有行。

虽然它正在这样做,但您可能正在点击UI中的其他内容,但您的线程&#34;一次只能做一件事&#34;。它忙着在循环中做这些事情。因此,当您点击时,您只需要等待。

那么如何修复它以便UI不会冻结?

用简单的英语,你需要这样做。想象一下你是一个主题:

我是UI线程,我的最终目标是保持UI响应。我不希望UI冻结。因此,如果我需要做除UI工作之外的任何事情,我会要求其他人这样做。当其他人正在做其他工作时,我可以自由地进行UI工作。

其他人是另一个主题。这是一个示例,请在代码中阅读我的注释并将其应用到您的应用程序中。如果要运行此代码,请创建一个带有名为Form1标签的表单label1和两个按钮。将ButtonClickHandlerAsync分配给一个按钮的点击处理程序,将Stop_Click分配给另一个按钮。

单击执行ButtonClickHandlerAsync的按钮时,整个操作开始。当它正在工作时,您可以单击其他按钮,它将显示一个消息框并保持响应。顺便说一下,我从here复制了这段代码,但我在代码中添加了自己的注释,这样你就知道发生了什么。

public partial class Form1 : Form {

   // We need this because this will allow us to interact with UI controls. UI controls can only be accessed by the thread that 
   // created the UI control. In this case it is the thread which started the application so the main thread.
   private readonly SynchronizationContext synchronizationContext;
   private DateTime previousTime = DateTime.Now;

   public Form1() {
      InitializeComponent();
      synchronizationContext = SynchronizationContext.Current;
   }

   private void Stop_Click(object sender, EventArgs e) {

      // I am the UI thread. I can do this because T2 is helping me do the loop.
      MessageBox.Show( "I am doing other things." );
   }

   private async void ButtonClickHandlerAsync(object sender, EventArgs e) {
      button1.Enabled = false;
      var count = 0;

      // I am the UI thread. I have other things to do. So please run this loop by using a thread from the thread pool.
      // When you are done running the loop let me know (This is what the await does)
      // I am the UI thread so I am going to return back from right here
      // to the point where ButtonClickHandlerAsync was called from. (it was called by a click, so when it returns it will have nothing
      // to do. Therefore, it will be ready to react to another UI job such as another click or update the UI etc.
      await Task.Run( () =>
      {
         // I am a thread from the thread pool. My name is T2. I am helping the UI thread so the UI thread can do other things.
         for( var i = 0; i <= 5000000; i++ ) {
            UpdateUI( i );
            count = i;
         }
      } );


      // I am the UI thread. Ok looks like the loop is done. So I will do the following 2 lines of work
      label1.Text = @"Counter " + count;
      button1.Enabled = true;
   }

   public void UpdateUI(int value) {

      // I am T2. I am helping the UI thread.
      var timeNow = DateTime.Now;

      if( ( DateTime.Now - previousTime ).Milliseconds <= 50 )
         return;

      // I do not have access to the UI controls since I did not create them. So I am just going to ask the synchronizationContext
      // to do this for me by giving it a SendOrPostCallback
      synchronizationContext.Post( new SendOrPostCallback( o =>
      {
         // I am the UI thread. I will do this.
         label1.Text = @"Counter " + ( int ) o;
      } ), value );

      // I am T2. I will do this and then return and do more work.
      previousTime = timeNow;
   }

如何修复代码?

您可以执行CheckOffice并使用线程池中的线程复制文件。如果需要与UI交互,该线程可以使用synchronizationContext。当线程池中的线程检查办公室并复制可能需要很长时间的文件时,主UI线程可以保持自由做其他事情,特别是如果文件很大的话。

&#34;我是UI线程。我没有时间等待ping回复。&#34; &#34;我是UI线程。我没有时间将文件从一个位置复制到另一个位置,这可能需要几秒或几分钟。我的工作是保持用户界面的响应能力。&#34;

修改

我在OP写入.NET 4的限制之前写了上面的答案。但我很确定他们为此创建了一个NuGet包。请参阅herehere

如果您无法使用asyncawait,则上述相同的概念适用于线程。

Thread t2 = new Thread( new ThreadStart(() =>
{
   for( var i = 0; i <= 5000000; i++ ) {
      UpdateUI( i );
      count = i;
   }
} ) );
t2.Start();