Windows 10 LTSB и C# Winforms - проблема потоковой передачи - PullRequest
0 голосов
/ 04 августа 2020

Итак, моя проблема здесь проста. Я разработал приложение WinForms, и оно хорошо работает на моей машине (Win7) и, фактически, на других машинах, но когда я запускаю приложение на машине Windows 10 2016 LTSB, мои фоновые потоки не работают должным образом - пока некоторые работают так, как ожидалось.

Поток приложения:

  • Подождите 1 минуту (obj1 с Threading.Timer)
  • Событие публикации (строковое сообщение от obj1, когда MainWindow вызывает obj1)
  • Текст формы обновления (с информацией из сообщения о событии)
  • Выполнение операции (фоновый поток)
  • Сообщение сообщения о событии (из фонового потока в MainWindow)
  • Случайный период ожидания (obj1 с Threading.Timer)
  • Событие публикации (строковое сообщение от obj1)
  • Форма обновления
  • Подождите 1 минуту

Теперь по некоторым политикам / причинам конфиденциальности я не могу поделиться точными вещами, которые здесь работают и как структурированы классы, но вот рудиментарная структура классов:

class MainWindow
{
    List<Controller> controllers = new List<Controller>();
    List<ControllerDisplay> controllerDisplays = new List<ControllerDisplay>();
    Queue<string> requests = new Queue<string>();
    
    private void AppLifetimeLoopCallback(object state)
    {
        while (requests.Count > 0)
        {
            string request = requests.Dequeue();
            string response = controllers[i].ProcessRequest(request);
            string anotherResponse = controllerDisplays[i].ProcessResponse(response);
            if (!string.NullOrWhiteSpace(anotherResponse))
            {
                requests.Enqueue(anotherResponse);
            }
        }
        
        for (int i = 0; i < controllers.Count; i++)
        {
            requests.Enqueue("STATE?");
        }
        
        timer.Change(300, Threading.Timeout.Infinite);
    }
}

class Controller
{
    public string ProcessRequest(string request)
    {
        switch (request)
        {
            case "STATE?":
              if (shouldRequest)
              {
                  return "REQ:1234";
              }
              else if (isProcessing)
              {
                  return "PRQ:1234";
              }
              else
              {
                  return "IDLE";
              }
              
              break;

            case "APPROVE":
              shouldRequest = false;
              isProcessing = true;
              thread = new Threading.Thread(new ThreadStart(() =>
              {
                  Threading.Thread.Sleep(300);
                  isProcessing = false;
                  return "RQF:1234";
              })
              {
                  IsBackground = true,
              };
              thread.Start();
              
              break;

            case "DENY:
              shouldRequest = false;
              break;
        }
    }
}

class ControllerDisplay
{
    public string ProcessResponse(string response)
    {
        switch (request.Substring(0, 4))
        {
            case "REQ:":
              thread = new Threading.Thread(new ThreadStart(() => 
              {
                  // perform some checks
                  if (isValid)
                  {
                     return "APPROVE";
                  }
                  else
                  {
                      return "DENY";
                  }
              })
              {
                  IsBackground = true,
              };
              thread.Start();
              
              break;

            case "RQF:":
              thread = new Threading.Thread(new ThreadStart(() =>
              {
                  // finalize and cleanup request bits
                  return "APPROVE";
              })
              {
                  IsBackground = true,
              };
              thread.Start();
              
              break;

            case "PRQ:":
              // update UI
              break;
        }
    }
}

Теперь, во-первых, я знаю, что это кажется т o может быть некоторое несоответствие между миллисекундной задержкой в ​​коде и описанием потока - однако обратите внимание, что есть другое Thread в Controller, которое переключает значение shouldRequest в этот минутный интервал, который переключает ответные сообщения на выполнить «запрос» при запросе состояния устройства.

Во-вторых, я также зарегистрировался в UnhandledException, а также ThreadException событиях приложения, которые должны регистрировать любое нежелательное поведение, которое произошло.

В-третьих, обратите внимание, что в MainWindow есть Threading.Timer (не в коде - я знаю), который обновляет пользовательский интерфейс с текущей датой и временем каждую секунду.

Теперь проблема в том, что я Заметил, что на машине Win10LTSB2016 приложение перестает выполнять фоновые операции. Некоторые из потоков, должно быть, только что умерли или что-то в этом роде, например, дата и время продолжают обновляться, как ожидалось, но один контроллер будет застрять в состоянии запроса, а другой - в состоянии завершения запроса - и сообщений об ошибках не будет / MessageBox. Обратите внимание, что машина не go в какое-либо состояние сна или гибернации в этот период, когда потоки просто останавливаются, и еще одно примечание заключается в том, что количество сокетов памяти равно 1, а не 2 (поскольку я читал, что это может повлиять на потоки, теряющие связь с друг друга, если они разделены на разные группы процессоров и ваше приложение не написано для обработки этого).

Закрытие: обратите внимание, что когда я выполняю проверки, чтобы увидеть, должен ли я, т.е. запустить поток процесса запроса в Controller, чтобы не выполнять один и тот же запрос снова и снова, пока не будет обнаружено изменение состояния, я делаю следующее:

lock (checkLock)
{
    if (isProcessingRequest)
    {
        break;
    }
    else
    {
        lock (resourceLock)
        {
            isProcessingRequest = true;
        }
    }
}

thread = new Threading.Thread(new ThreadStart(() =>
{
    lock (resourceLock)
    {
        // finalize and cleanup request bits
        isProcessingRequest = false;
    }
    
    return "APPROVE";
})
{
    IsBackground = true,
};
thread.Start();

1 Ответ

0 голосов
/ 05 августа 2020

Я закрываю этот вопрос, так как это полная хогва sh, и я прошу прощения за время, потраченное на чтение этого.

Таким образом, ручные блокировки на Queue<T> продолжали вызывать тупик раньше , таким образом они были удалены. Сначала казалось, что проблема решена, но длительные тесты время от времени доказывали (что я считал проблемой Windows) возникла тупиковая ситуация.

Причина, по которой я думал, что это Windows проблема в том, что кто-то сказал мне, что это то, что они испытали на Windows, и это определенно проблема Windows. Ни на кого не смотрит свысока, но он не выполняет многопоточность, поскольку не знает, как это сделать, и упомянутый результат был результатом его попытки выполнить многопоточность. Урок, извлеченный здесь.

Спасибо, ребята и / или девушки.

EDIT: проблема решена, и длительные тесты пока выглядят очень многообещающими. Для этого я просто изменил Queue<T> на ConcurrentQueue<T> и внес несколько изменений в код там, где это необходимо (т.е. ConcurrentQueue<T>.Clear() не существует там, где Queue<T>.Clear() есть).

...