尝试更新datagridview时接口冻结

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

我已经搜索了很多我知道使用task.run(); 是不可能的,因为它不包含在.not 4它是.net 4.5的新function我也知道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); } } 

我也试过这个但是正如我所说的那样在网络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  { 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); } } 

在此处输入图像描述

在此处输入图像描述

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

答案很长

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

虽然它正在这样做,你可能在UI中点击其他东西,但你的线程“一次只能做一件事”。 它忙于在循环中完成这些工作。 因此,当您点击时,您只需要等待。

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

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

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

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

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

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

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

编辑

我在OP写入.NET 4的限制之前写了上面的答案。但我很确定他们为此创建了一个NuGet包。 看到这里和这里 。

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

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