本文介绍了尝试更新 datagridview 时界面冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using below code to copy files and set status column at datagridview to inform user that connection is up but when I press the button to execute ismethod the interface freeze...

我已经搜索了很多我知道使用 task.run(); 是不可能的,因为它不包含在 .not 4 它是 .not 4 的一个新功能code>.net 4.5 我也知道 Task.Factory.StartNew(); 可以用来代替 task.run() 但它作为隐式线程有很大的风险和我知道使用显式线程也是不错的选择

I have searched a lot i know that using task.run(); is not possible since it is not included in .not 4 it a new feature of .net 4.5 i also know that Task.Factory.StartNew(); can be used instead of using task.run() but it has a lot of risk as implicit thread and i know that using explicit threading is also good choose a

我想要一些帮助来继续我的项目并继续学习而不是在那个无聊的点上堆积

i want a little help to go on with my project and keep learning instead of getting stacked at that boring point

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	est" + 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;
        }
    }

下面是我尝试制作线程的方式,但这不能解决我的问题

below how I have tried to make thread but that wont solve my problem

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	est" + 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);
    }
}

我也试过这个,但正如我所说的 thask.run 在 dot net 4 上不可用

I have also tried this but as i said thask.run is not available at dot net 4

    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	est" + Path.GetFileName(SoruceFileNamePath);
                    string DestinationFileNamePath = @"F:	est" + 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方法...

The UpdateUI method to update UI...

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

推荐答案

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

Short answer: There is nothing wrong with your code. The design is not good.

长答案

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

Have you heard someone say "I can only do one thing at a time!" Well that is what is going on here. Your windows forms application's code is being executed by 1 thread and that thread can only do one thing at a time. When it is pinging, it waits for the reply. If the reply is successful it then copies a file. Since you have a loop it keeps doing this until it has completed all rows.

这样做时,您可能正在单击 UI 中的其他内容,但您的线程一次只能做一件事".它正忙着做循环中的事情.因此,当您点击时,您只需等待.

While it is doing that, you are probably clicking other things in the UI, but your thread "can only do 1 thing at a time". It is busy doing the stuff in the loop. Therefore, when you are clicking, you just have to wait.

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

用简单的英语,你需要这样做.想象你是一个线程:

In plain English, you need to do this. Imagine you are a thread:

我是 UI 线程,我的最终目标是保持 UI 响应.我不希望用户界面冻结.因此,如果我需要做除 UI 工作之外的任何事情,我会请其他人来做.当别人在做其他工作时,我可以自由地做 UI 工作.

I am the UI thread and my ultimate goal is to keep the UI responsive. I do not want the UI to freeze. Therefore, if I need to do anything except UI work, I am going to ask someone else to do it. While that someone else is doing other work, I will be free to do UI work.

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

That someone else is another thread. Here is an example, read my comments in the code and apply it to your application. If you want to run this code, create a form Form1 with a label named label1 and two buttons. Assign ButtonClickHandlerAsync to one button's click handler and Stop_Click to the other button.

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

The whole action starts when you click the button which executes ButtonClickHandlerAsync. While it is doing work, you can click the other button and it will show a message box and stay responsive. By the way I have copied this code from here but I added my own comments inside the code so you know what is going on.

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 线程可以自由地做其他事情,尤其是在文件很大的情况下.

You can do CheckOffice and copying the files using a thread from the threadpool. That thread can use the synchronizationContext to if it needs to interact with the UI. The main UI thread can stay free to do other things while the thread from the thread pool is checking the office and copying a file which could take a long time, especially, if the file is big.

我是 UI 线程.我没有时间等待 ping 回复."我是 UI 线程.我没有时间将文件从一个位置复制到另一个位置,这可能需要几秒钟或几分钟.我的工作是保持 UI 响应."

编辑

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

I wrote the above answer before the OP wrote the restriction of .NET 4. But I am pretty sure they have created a NuGet package for this. See here and here.

如果您不能使用 asyncawait,上述相同的概念也适用于线程.

If you cannot use async and await, the same concepts above apply to threading.

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

这篇关于尝试更新 datagridview 时界面冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 00:49