Я использую приведенный ниже код для копирования файлов и установки столбца состояния в 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);
        }
    }

enter image description here

enter image description here

0
sam 21 Дек 2016 в 23:13
Выполните отладку и посмотрите, какая строка проблемная. Может быть CheckOffice().
 – 
Mat
21 Дек 2016 в 23:17
Я публикую свой пост и отправляю код CheckOffice (), я не думаю, что это проблема, пожалуйста, помогите мне, я хочу знать
 – 
sam
21 Дек 2016 в 23:19
Google "async await" или "многопоточность"
 – 
Steve
21 Дек 2016 в 23:25
2
Есть много вопросов о "зависании пользовательского интерфейса". Ваш пост не отражает всех исследований, которые вы провели перед тем, как задать вопрос. Это, скорее всего, приведет к отрицательным голосам. Обратите внимание, что просьба «не голосовать против» не добавляет к сообщению никакой полезной информации и потенциально просто увеличивает количество голосов против (так что я это отредактировал).
 – 
Alexei Levenkov
21 Дек 2016 в 23:25
Пользовательский интерфейс будет заморожен с момента начала операции до момента ее завершения. И это будет верно для любой синхронной операции. На полпути нет точки, где пользовательский интерфейс зависает.
 – 
Servy
21 Дек 2016 в 23:31

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();
4
CodingYoshi 22 Дек 2016 в 02:14
Спасибо за ваше очень полезное объяснение, но я думаю, и я с сожалением должен сказать, что, как я сказал в своем исходном посте, Task.Run () недоступен в dot net v4, его новой функции dot net v4.5 и использовании Task. Factory.StartNew (); есть риск, как я обнаружил, когда искал, поэтому этот пример частично неприменим, я прав?
 – 
sam
22 Дек 2016 в 01:52
См. мое редактирование. Честно говоря, это та же идея.
 – 
CodingYoshi
22 Дек 2016 в 02:15
Извините за поздний ответ вам, мой друг, я все время тратил на то, чтобы разобраться в кейсе и построить его, и я очень ценю вашу помощь ... пожалуйста, я обновил свой пост, новый код ... пожалуйста, если вы хотите принять посмотрите и посоветуйте, если что не так ... Спасибо ..
 – 
sam
22 Дек 2016 в 23:03
Не вызывайте updateui, если результат 1. Нет пользовательского интерфейса для обновления. Если результат равен 1, вы хотите скопировать файл. Сделайте это в потоке, или пользовательский интерфейс снова зависнет, пока копия не будет завершена.
 – 
CodingYoshi
23 Дек 2016 в 02:16
Предложенный вами способ работает как шарм, и интерфейс не зависает, даже если я обновляю пользовательский интерфейс в обоих случаях, когда vResult равен 1 или vResult равен 0, но теперь я ищу странное мнение, что не все строки в состоянии столбца поднялись до подключенного или отключен в соответствии со значением vResult ... похоже, мне нужно снова нажать кнопку, чтобы получить этот результат для остальных строк ... как вы можете видеть это на снимке экрана 2, он обновляет только 5 строк
 – 
sam
23 Дек 2016 в 14:48