Очередной вопрос про фоновое выполнение функций?

Привет.

Я совсем не специалист в шарпе, более того, шарп — не основной мой язык (использую его только для программ, строго требующих GUI) и никак не пойму, как же все-таки делать правильно.


Что требуется: выполнение любых действий не в основном потоке, т.е. полностью исключить подвисание интерфейса.


Программа работает исключительно с сетью, скорости интернета на клиентах разные, многие жалуются.

При этом надо постоянно взаимодействовать с полученными данными и делать это последовательно, отображать изменения в GUI и т.д.


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


Не могли бы вы поделиться примерами кода, в котором какие-нибудь типичные синхронные конструкции (например получение данных, парсинг, отправка HTTP-запроса, обновление интерфейса основного окна, опять получение и дальнейший парсинг) сделаны в фоне, а не просто разбиты на функции? Или хотя бы просто поясните, как сделать это с минимальными затратами и максимальной понятностью (т.е. чтобы чтение алгоритма не прерывала простыня взаимодействия с основной формой). Спасибо заранее.
  • Вопрос задан
  • 4474 просмотра
Решения вопроса 1
Illivion
@Illivion
Для операций в фоне советую использовать асинхронные операции. Если это сеть, то, например BeginSend и BeginWrite. В колбэках прописываем реакцию в GUI. Для того, чтобы операция выполнилась в GUI-потоке используем SynchronizationContext. Вот такой простой пример:

public partial class SimpleForm : Form
    {
        private readonly SynchronizationContext _context;

        private readonly TcpListener _serverListener;

        private int _connectionsCount;

        public SimpleForm()
        {
            InitializeComponent();

            // Зафиксировали GUI-поток
            _context = SynchronizationContext.Current;

            // Включаем ожидание входящих соединений
            _serverListener = new TcpListener(IPAddress.Loopback, 30000);
            _serverListener.Start();
            _serverListener.BeginAcceptTcpClient(AcceptCallback, null);
        }

        private void AcceptCallback(IAsyncResult ar)
        {
            // Поймали соединение. Завершаем.
            var client = _serverListener.EndAcceptTcpClient(ar);

            // Сообщаем о новом подключении.
            AddConnection();
        }

        private void AddConnection()
        {
            // Увеличиваем счетчик подключений
            Interlocked.Add(ref _connectionsCount, 1);

            // Сообщаем лейблу на форме новое значение. Используем GUI-поток, ибо в Callback'е находимся в чужом потоке из пула.

            _context.Post(state => { lbConnCount.Text = _connectionsCount.ToString(); }, null);
        }
    }
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
В C# 5.0 и .NET 4.0 появиились замечательные async\await, полностью убирающие простынки коллбэков. Почитайте про них.
Ответ написан
Комментировать
@codecity
Или хотя бы просто поясните, как сделать это с минимальными затратами и максимальной понятностью


Наверное вопрос аналогичен «какой тип использовать в C++ для строк». В этом отношении C# так разросся, столько вариантов один другого краше — что сложно дать однозначный совет.

Проще всего это, конечно, новая констркция async + await. Но в C#4 нужно доустанавливать AsyncCTP.
Ответ написан
@brainx64
Я во времена .NET 2.0 для фоновых задач использовал System.ComponentModel.BackgroundWorker
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы