C# 尝试更新datagridview时接口冻结

C# 尝试更新datagridview时接口冻结,c#,datagridview,C#,Datagridview,我正在使用下面的代码复制文件,并在datagridview处设置status列,以通知用户连接已打开,但当我按下按钮执行时,连接已关闭 方法界面冻结 我已经搜索了很多我知道使用task.run()是不可能的,因为它不包括在中.not 4它是.net 4.5的一个新功能,我还知道Task.Factory.StartNew()来代替task.run(),但它作为隐式线程有很大的风险,我知道使用显式线程也是一个不错的选择 我想得到一些帮助,继续我的项目,继续学习,而不是在那个无聊的时刻堆积起来 pub

我正在使用下面的代码复制文件,并在
datagridview
处设置status列,以通知用户连接已打开,但当我按下按钮执行时,连接已关闭 方法界面冻结

我已经搜索了很多我知道使用
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);
    }
}
我也尝试过,但正如我所说,thask.run在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\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);
        }
    }
public void UpdateUI(int-vResult、int-RowNum、string-SoruceFileNamePath、string-DestinationFileNamePath、string-OfficeIPAddress)
{
尝试
{
var timeNow=DateTime.Now;
if((DateTime.Now-PreviousTime).ms
{
如果(vResult==1)
{
DGV_OfficeList[4,RowNum].Value=“已连接”;
//Copy(SoruceFileNamePath,DestinationFileNamePath,true);
//MessageBox.Show(“Pingable”+OfficeIPAddress);//TestPurpose
}
否则如果(vResult==0)
{
DGV_OfficeList[4,RowNum].Value=“已断开”;
//MessageBox.Show(“不可访问”);//TestPurpose
}
}),vResult);
以前的时间=现在的时间;
}
捕获(例外情况除外)
{
MessageBox.Show(例如Message,“Error”,MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}


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

长答案

你听到有人说“我一次只能做一件事!”这就是这里发生的事情。windows窗体应用程序的代码由一个线程执行,该线程一次只能执行一件事。当它ping时,它会等待回复。如果回复成功,则复制一个文件。因为您有一个循环,所以它会一直这样做,直到完成所有行

当它这样做的时候,你可能正在点击UI中的其他东西,但是你的线程“一次只能做一件事”。它忙着做循环中的事情。因此,当您单击时,您只需等待

那么如何修复它,使UI不会冻结?

简单地说,你需要这样做。想象你是一根线:

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

其他人是另一个线索。下面是一个示例,请阅读我在代码中的注释并将其应用于您的应用程序。如果要运行此代码,请使用名为
label1
的标签和两个按钮创建一个表单
Form1
。将
按钮ClickHandlerAsync
分配给一个按钮的单击处理程序,并将
停止\u单击
分配给另一个按钮

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

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

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

长答案

你听到有人说“我一次只能做一件事!”这就是这里发生的事情。windows窗体应用程序的代码由一个线程执行,该线程一次只能执行一件事。当它ping时,它会等待回复。如果回复成功,则复制一个文件。因为您有一个循环,所以它会一直这样做,直到完成所有行

当它这样做的时候,你可能正在点击UI中的其他东西,但是你的线程“一次只能做一件事”。它忙着做循环中的事情。因此,当您单击时,您只需等待

那么如何修复它,使UI不会冻结?

简单地说,你需要这样做。想象你是一根线:

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

其他人是另一个线索。下面是一个示例,请阅读我在代码中的注释并将其应用于您的应用程序。如果要运行此代码,请使用名为
label1
的标签和两个按钮创建一个表单
Form1
。将
按钮ClickHandlerAsync
分配给一个按钮的单击处理程序,并将
停止\u单击
分配给另一个按钮

当您单击执行
按钮ClickHandlerAsync
的按钮时,整个操作开始。当它工作时,您可以单击另一个按钮,它将显示一个消息框并保持响应。顺便说一句,我从中复制了这段代码,但我在代码中添加了我自己的注释,所以您知道goi是什么
Thread t2 = new Thread( new ThreadStart(() =>
{
   for( var i = 0; i <= 5000000; i++ ) {
      UpdateUI( i );
      count = i;
   }
} ) );
t2.Start();