Управление потоками в приложении .NET WinForm

#c# #.net #winforms #multithreading #user-interface

#c# #.net #winforms #многопоточность #пользовательский интерфейс

Вопрос:

Вот моя цель: приложение состоит из формы — MainForm — которая содержит множество вкладок (похожих на графический интерфейс Notepad ). Каждая вкладка является дочерним элементом UserControl (UCDx). MainForm может отображать 2 или более вкладок, которые содержат экземпляры одного и того же UCDx. Цель состоит в том, чтобы UCDx должен был выполнять некоторые трудоемкие действия или выполнение методов, то есть чтение / запись в БД, вызовы методов веб-службы. Такого рода действия я бы хотел выполнять в отдельных потоках, чтобы не замораживать пользовательский интерфейс. Также мне нужно будет реализовать информационную форму (InfoForm), которая будет отображать информацию о текущих запущенных потоках с некоторой функциональностью взаимодействия, то есть закрывать потоки, которые выполняются слишком долго.

Основными проблемами, с которыми я столкнулся, являются:

  • взаимодействие потока с графическим интерфейсом — т. Е. Поток считывает данные, которые должны отображаться в таблице;
  • управление взаимодействием потоков — после внедрения InfoForm.

В совместном / параллельном программировании я новичок — может кто-нибудь дать некоторые подсказки о том, как это реализовать?

Ответ №1:

Я бы сначала отделил вашу рабочую логику от пользовательского интерфейса.

Например, для «потока, которому необходимо считывать данные и отображать их», попробуйте разделить вашу проблему на более мелкие части:

  1. Создайте простой класс, который имеет метод синхронной обработки, без использования потоков. Что-то вроде:

     interface IServiceReader
    {
         Data GetDataFromService(IServiceInfo info);
    }
      
  2. Протестируйте это. Создайте модульные тесты или даже небольшое консольное приложение, где вы увидите, что оно отлично работает без графического интерфейса.

  3. Создайте класс асинхронной оболочки, который вызывает ServiceReader для выполнения задания, но вызывает его в фоновом потоке и запускает событие по завершении.

     interface IAsyncServiceReader
    {
         void Start(IServiceInfo info);
         event EventHandler<Data> DataReceived;
    }
      
  4. Снова протестируйте это. Это должна быть отдельная библиотека классов, независимая от вашего графического интерфейса.

  5. Используйте MVC или аналогичный шаблон, чтобы отделить ваши представления (вкладки) от вашей модели (async service reader). Это означает, что вы должны запускать события взаимодействия из своих представлений, обрабатывать их в контроллере, а затем позволять контроллеру вызывать соответствующее действие в модели:

     class Controller
    {
         readonly ITabView _view; 
         readonly IAsyncServiceReader _model;
         public Controller(ITabView view, IAsyncServiceReader model)
         {
              _view = view; _model = model;
              AttachHandlers();
         }
    
         void AttachHandlers()
         {
              view.UserRequestedLoading  = (sender,info) => model.Start(info);
              model.DataReceived  = (sender,data) => view.DisplayData(data);
         }
    }
      
  6. Наконец, убедитесь, что вызовы фонового потока отправляются в нужный поток (GUI). Это просто означало бы, что в вашем представлении вкладок необходимо проверить, требуется ли вызов:

     class TabView : UserControl
    {
         void DisplayData(Data data)
         {
             if (this.InvokeRequired)
             {
                 BeginInvoke(new Action<Data>(DisplayData), data);
                 return;
             }
    
             // otherwise, display the data in some way
             dataGrid.DataSource = data; ...
         }
    }
      

Отделение модели от представления упростит тестирование и последующие изменения пользовательского интерфейса. Вы можете решить сделать это консольным приложением или службой Windows, и это сохранит вашу бизнес-логику нетронутой.

Ответ №2:

Используйте вызов из фоновых потоков для выполнения действий в графическом интерфейсе. Существует множество решений для многопоточности: задачи, фоновые рабочие, пользовательские потоки, пул потоков. Вы также можете использовать асинхронные операции.

Ответ №3:

хорошо, ребята … после прочтения большого количества информации по этой проблеме (наиболее важные документы Albahari threads) — вот первая попытка реализации моих мыслей — возьмите через svn из gCode. Пожалуйста, прокомментируйте как можно больше, ребята — мне нужны наши мнения и наблюдения. Спасибо.