我使用下面的代码复制文件并在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);
}
}
答案 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包。请参阅here和here。
如果您无法使用async
和await
,则上述相同的概念适用于线程。
Thread t2 = new Thread( new ThreadStart(() =>
{
for( var i = 0; i <= 5000000; i++ ) {
UpdateUI( i );
count = i;
}
} ) );
t2.Start();