Дизайн: Task-Asynchronous Pattern (TAP с await / async), против потоков с сигнализацией против других структур потоков

1

Было бы с благодарностью принята помощь с идеями о редизайне ниже программы С#. Я пытаюсь выбрать между реализацией многопоточности, используя 1) TAP, 2) потоки конечных потоков, которые содержат прядильщики, которые заканчиваются, когда их bools установлены в false, или 3) одни и те же потоки с использованием сигнализации вместо этих bools. Я объясню нижеприведенную программу, чтобы сделать это ясно.

Программа

Программа - приложение для автоматизации игры на С#, которое я разрабатываю как интересный способ изучения языка, а функции С# (5.0) лучше. Он имеет пользовательский интерфейс, который должен оставаться отзывчивым во время работы приложения.

Когда открывается отдельная вкладка в пользовательском интерфейсе, программа запускает новый поток под названием "Сканирование", который в новом методе в другом классе сканирует различные ячейки памяти и обновляет метки в пользовательском интерфейсе с помощью этих быстро изменяющихся значений с помощью интерфейса синхронизации SyncContext, Это продолжается в течение (сканирующего) цикла, пока сканирование bool истинно (как правило, полная продолжительность жизни программы).

Когда пользователь нажимает кнопку "Пуск" на этой вкладке, программа запускает два новых потока, которые выполняют следующие действия: "Прогон" перемещает символ вокруг по определенному пути. Тема "Действие" нажимает определенные кнопки и выполняет действия одновременно с тем, как игрок запускает путь. Если возникает определенный сценарий, программа должна временно приостановить поток и поток действий, запустить метод, а когда он закончится, вернитесь к запуску и действию.

Когда пользователь нажимает кнопку "Стоп" на этой вкладке, автоматизация должна останавливаться, и потоки завершаются.

Соревнование

Я уже создал рабочую версию, используя непрерывные циклы сглаживания в каждом потоке, который заботится о различных работах. Прокручиватели работают с помощью while (myBool). Для трех потоков: bools: сканирование, запуск и действие.

Когда я хочу остановить поток, я устанавливаю bool в false и использую Thread.Join, чтобы ждать, пока поток завершится изящно, прежде чем продолжить. Потоки могут, как упоминалось, останавливаться пользователем, нажав кнопку "Стоп", или автоматически программой в рамках ее функциональности. В последнем случае поток останавливается, присоединяется, а затем на более позднем этапе перезапускается.

Проделав много чтения и исследований по потоковому использованию и новым инструментам асинхронного программирования на С# 5.0, я понял, что способ, которым я в настоящее время это делаю, может быть очень неуклюжим и непрофессиональным. Он создает множество проблем с синхронизацией/потоковой безопасностью, и поскольку цель всего этого состоит в том, чтобы узнать больше о С#, я хотел бы принять ваше решение о том, следует ли мне изменить его на мелкозернистый асинхронный подход к программированию, используя TAP с асинхронно и ждать по мере необходимости.

Это похоже на случай, когда могут быть полезны задачи с маркерами отмены? Потоки после всех длительных операций, поэтому я был обеспокоен тем, что использование пула потоков (Task.Run) приведет к плохой гигиене в пуле потоков (чрезмерной подписке). Если асинхронное программирование похоже на плохое совпадение здесь, как об использовании потоков, как я это сделал, но вместо этого используйте сигнализацию для запуска и остановки потоков?

Любые мысли очень оценили.

  • 0
    Рассматривали ли вы использование TPL DataStream или ReactiveExtensions? Оба предназначены для одновременной и / или асинхронной передачи данных.
Теги:
multithreading
async-await
asynchronous
unmanaged

1 ответ

0

Нет. TPL был разработан для выполнения более коротких задач, когда распределение новых потоков на все время вредно для производительности. Он получил довольно приятные функции, такие как очереди заданий и кражу работы (поток TPL может принимать задания из другого потока). Конечно, у него может быть более длительная работа, но вы не получите столько преимуществ от этого. На противоположной стороне вы вынуждаете TPL выделять новые потоки.

Однако этот вопрос является немного общим в том смысле, что нам нужна дополнительная информация о вашей фактической реализации, чтобы знать, что вы должны использовать. Для потока сканирования совершенно очевидно, что он должен работать в одном потоке.

Но для других это трудно понять. Они работают постоянно или периодически? Если они все время работают, вы должны держать их в отдельных потоках.

Что касается синхронности потока, то есть еще одна альтернатива. Вы можете использовать ConcurrentQueue для очереди всего, что нужно сделать. Таким образом, вам не нужна синхронизация. Просто пусть поток пользовательского интерфейса проверяет очередь и рисует что-нибудь в нем, в то время как производители могут продолжать выполнять свою работу.

Фактически, таким образом вы можете перемещать все, что не связано с рисованием пользовательского интерфейса, на другие потоки. Это также должно улучшить отзывчивость в вашем приложении.

public void ActionRunnerThreadFunc()
{
    _drawQueue.Enqueue(new SpaceShipRenderer(x, y));
}

public void UIThreadFunc()
{
    IItemRender item;
    if (_drawQueue.TryDequeue(out item))
        item.Draw(drawContext);
}
  • 0
    Спасибо за ответ J. Приложение на самом деле не игра, а скорее инструмент, который автоматизирует игровой процесс (поэтому перемещает персонажа и выполняет различные задачи, без взаимодействия с человеком). Я думал, что задачи кажутся полезными, так как они могут иметь тип возврата, а также предлагают возможность прикреплять продолжения (либо явно с помощью awaiter, либо неявно через ключевое слово await ). Поэтому я думаю, что вы правы, что сканер памяти должен просто работать в отдельном потоке и может использовать ManualResetEvent.WaitOne вместо bool сканера. (продолжение в следующем комментарии)
  • 0
    Но для двух других потоков: они не работают вечно. Скажем, что я хочу сделать, это рубить деревья. Таким образом, в одном потоке персонаж бегает по заданному пути. Другой поток, который работает параллельно, постоянно нажимает кнопку для поиска деревьев. Как только дерево найдено, оба этих потока должны временно остановиться, и должен начаться метод treeChopping. Когда дерево завершит рубку, игрок должен вернуться к бегу по дорожке в поисках деревьев. Такое ощущение, что три задачи будут хорошо работать здесь, верно?
Показать ещё 8 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню