Я использую приведенный ниже код для копирования файлов и установки столбца состояния в 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);
}
}
Я тоже пробовал это, но, как я уже сказал, 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);
}
}
Метод UpdateUI для обновления 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);
}
}
1 ответ
Краткий ответ: с вашим кодом все в порядке. Дизайн плохой.
Длинный ответ
Вы слышали, как кто-то сказал: "Я могу делать только одно дело за раз!" Что ж, вот что здесь происходит. Код вашего приложения Windows Forms выполняется одним потоком, и этот поток может делать только одно действие за раз. Когда он пингует, он ждет ответа. Если ответ успешен, он копирует файл. Поскольку у вас есть цикл, он продолжает делать это, пока не завершит все строки.
В то время как он это делает, вы, вероятно, щелкаете другие элементы в пользовательском интерфейсе, но ваш поток «может делать только одно действие за раз». Он занят выполнением задач в цикле. Поэтому, когда вы нажимаете, вам просто нужно подождать.
Итак, как мне исправить это, чтобы пользовательский интерфейс не зависал?
Говоря простым языком, вам нужно это сделать. Представьте, что вы - нить:
Я - поток пользовательского интерфейса, и моя конечная цель - сохранить отзывчивость пользовательского интерфейса. Я не хочу, чтобы 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
и копировать файлы, используя поток из пула потоков. Этот поток может использовать synchronizationContext
, если ему нужно взаимодействовать с пользовательским интерфейсом. Основной поток пользовательского интерфейса может оставаться свободным для других дел, пока поток из пула потоков проверяет офис и копирует файл, что может занять много времени, особенно если файл большой.
«Я - поток пользовательского интерфейса. У меня нет времени ждать ответа на эхо-запрос». «Я - поток пользовательского интерфейса. У меня нет времени копировать файл из одного места в другое, что может занять секунды или минуты. Моя задача - поддерживать отзывчивость пользовательского интерфейса».
ИЗМЕНИТЬ
Я написал вышеупомянутый ответ до того, как OP написал ограничение .NET 4. Но я почти уверен, что они создали для этого пакет NuGet. См. здесь и здесь.
Если вы не можете использовать async
и await
, те же принципы, что и выше, применимы к потоковой передаче.
Thread t2 = new Thread( new ThreadStart(() =>
{
for( var i = 0; i <= 5000000; i++ ) {
UpdateUI( i );
count = i;
}
} ) );
t2.Start();
Похожие вопросы
Связанные вопросы
Новые вопросы
c#
C # (произносится как «резкий») - это высокоуровневый, статически типизированный язык программирования с несколькими парадигмами, разработанный Microsoft. Код C # обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, включая, среди прочего, .NET Framework, .NET Core и Xamarin. Используйте этот тег для вопросов о коде, написанном на C # или в формальной спецификации C #.
CheckOffice()
.