Было бы с благодарностью принята помощь с идеями о редизайне ниже программы С#. Я пытаюсь выбрать между реализацией многопоточности, используя 1) TAP, 2) потоки конечных потоков, которые содержат прядильщики, которые заканчиваются, когда их bools установлены в false, или 3) одни и те же потоки с использованием сигнализации вместо этих bools. Я объясню нижеприведенную программу, чтобы сделать это ясно.
Программа
Программа - приложение для автоматизации игры на С#, которое я разрабатываю как интересный способ изучения языка, а функции С# (5.0) лучше. Он имеет пользовательский интерфейс, который должен оставаться отзывчивым во время работы приложения.
Когда открывается отдельная вкладка в пользовательском интерфейсе, программа запускает новый поток под названием "Сканирование", который в новом методе в другом классе сканирует различные ячейки памяти и обновляет метки в пользовательском интерфейсе с помощью этих быстро изменяющихся значений с помощью интерфейса синхронизации SyncContext, Это продолжается в течение (сканирующего) цикла, пока сканирование bool истинно (как правило, полная продолжительность жизни программы).
Когда пользователь нажимает кнопку "Пуск" на этой вкладке, программа запускает два новых потока, которые выполняют следующие действия: "Прогон" перемещает символ вокруг по определенному пути. Тема "Действие" нажимает определенные кнопки и выполняет действия одновременно с тем, как игрок запускает путь. Если возникает определенный сценарий, программа должна временно приостановить поток и поток действий, запустить метод, а когда он закончится, вернитесь к запуску и действию.
Когда пользователь нажимает кнопку "Стоп" на этой вкладке, автоматизация должна останавливаться, и потоки завершаются.
Соревнование
Я уже создал рабочую версию, используя непрерывные циклы сглаживания в каждом потоке, который заботится о различных работах. Прокручиватели работают с помощью while (myBool). Для трех потоков: bools: сканирование, запуск и действие.
Когда я хочу остановить поток, я устанавливаю bool в false и использую Thread.Join, чтобы ждать, пока поток завершится изящно, прежде чем продолжить. Потоки могут, как упоминалось, останавливаться пользователем, нажав кнопку "Стоп", или автоматически программой в рамках ее функциональности. В последнем случае поток останавливается, присоединяется, а затем на более позднем этапе перезапускается.
Проделав много чтения и исследований по потоковому использованию и новым инструментам асинхронного программирования на С# 5.0, я понял, что способ, которым я в настоящее время это делаю, может быть очень неуклюжим и непрофессиональным. Он создает множество проблем с синхронизацией/потоковой безопасностью, и поскольку цель всего этого состоит в том, чтобы узнать больше о С#, я хотел бы принять ваше решение о том, следует ли мне изменить его на мелкозернистый асинхронный подход к программированию, используя TAP с асинхронно и ждать по мере необходимости.
Это похоже на случай, когда могут быть полезны задачи с маркерами отмены? Потоки после всех длительных операций, поэтому я был обеспокоен тем, что использование пула потоков (Task.Run) приведет к плохой гигиене в пуле потоков (чрезмерной подписке). Если асинхронное программирование похоже на плохое совпадение здесь, как об использовании потоков, как я это сделал, но вместо этого используйте сигнализацию для запуска и остановки потоков?
Любые мысли очень оценили.
Нет. 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);
}
await
). Поэтому я думаю, что вы правы, что сканер памяти должен просто работать в отдельном потоке и может использовать ManualResetEvent.WaitOne вместо bool сканера. (продолжение в следующем комментарии)