C#/.NET дизайн приложения с многопоточностью / задачами
Я хочу разработать приложение, которое обеспечивает некоторую двунаправленную связь между двумя совершенно разными системами. (мост)
Один из них может вызывать мое приложение через веб-сервисы, а другой - это аппаратное обеспечение сторонних производителей. Это говорит RS232. Используя приемопередатчик RS232<->ETH, мы можем общаться с аппаратной частью, используя TCP.
Программа имеет следующие требования.
- Существует главный поток, выполняющий "службу управления". Это может быть, например, конечная точка WCF или REST-служба webapi с собственным размещением. Он предоставляет методы для запуска новых рабочих экземпляров или получения списка всех рабочих экземпляров и их соответствующих состояний.
- Есть множество "рабочих" тем. Каждый из них имеет модель состояний с 5 состояниями.
- Например, в одном состоянии должен быть создан прослушиватель TCP для приема входящих подключений от подключенного аппаратного устройства (программирование на основе сокетов является обязательным). Как только он получает желаемую информацию, он отправляет ответ и переходит в следующее состояние.
- Должна быть возможность из основного (управляющего) потока (изящно) завершить отдельные рабочие потоки (например, если рабочий поток застрял в состоянии, в котором он не может восстановиться)
Вот откуда я родом:
- Я рассматривал сервисы рабочего процесса WCF (активность модели состояния), однако я не был уверен, как создать TcpListener там - и поддерживать его. Мне не нужно никакого рабочего процесса "приостановить / сериализовать и возобновить / десериализовать", как поведение.
- Основной поток, вероятно, не так уж и важен - он просто должен быть там и запущен. Меня беспокоят дочерние (фоновые) потоки и их внутренний конечный автомат.
- Я пытался сосредоточиться на том, как Задачи могут помочь здесь, но я решил, что темы на самом деле лучше подходят для этой задачи
Так как в.NET (4+) было много разработок, я не уверен, какой подход придерживаться... Интернет полон примеров с 2005 по 2010, которые, вероятно, не просто устарели. Очень трудно отделить DO от DONT.
Я рад за любые намеки.
ОБНОВЛЕНИЕ: Хорошо, я постараюсь уточнить, что мой вопрос...
Я думаю, что самый простой способ - предоставить псевдокод.
public static void Main()
{
// Start self-hosted WCF service (due to backwards compatibility, otherwise I'd go with katana/owin) on a worker thread
StartManagementHeadAsBackgroundThread();
// Stay alive forever
while(running)
{
// not sure what to put here. Maybe Thread.Sleep(500)?
}
// Ok, application is shutting down => somehow "running" is not true anymore.
// One possible reason might be: The management service's "Shutdown()" method is being called
// Or the windows service is being stopped...
WaitForAllChildrenToReachFinalState();
}
private static void StartManagementHeadAsBackgroundThread()
{
ThreadStarter ts = new ThreadStarter(...);
Thread t = new Thread(ts);
t.Start();
}
Глава управления (= служба wcf) предлагает несколько методов
- StartCommunicator() для запуска новых рабочих потоков, выполняющих реальную работу с 5 состояниями
- Shutdown(), чтобы завершить работу всего приложения, позволяя всем рабочим потокам завершиться изящно (обычно это вопрос нескольких минут)
- GetAllCommunicatorInstances () для отображения сводки всех рабочих потоков и текущего состояния, в котором они находятся.
- DestroyCommunicatorInstance (порт) для принудительного завершения рабочего потока - например, если коммуникатор застрял в состоянии, из которого он не может восстановиться.
В любом случае мне нужно создавать новые фоновые потоки из службы управления (метод StartCommunicator).
public class Communicator
{
private MyStateEnum _state;
public Communicator(int port)
{
_state = MyStateEnum.Initializing;
// do something
_state = MyStateEnum.Ready;
}
public void Run()
{
while(true)
{
// again a while(true) loop?!
switch(_state):
{
case MyStateEnum.Ready:
{
// start TcpListener - wait for TCP packets to arrive.
// parse packets. If "OK" set next good state. Otherwise set error state.
}
}
if(_state == MyStateEnum.Error) Stop();
break;
}
}
public void Stop()
{
// some cleanup.. disposes maybe. Not sure yet.
}
}
public enum MyStateEnum
{
Initializing, Ready, WaitForDataFromDevice, SendingDataElsewhere, Done, Error
}
Таким образом, вопрос в том, приведет ли мой подход меня куда-нибудь или я совершенно не на том пути.
Как мне реализовать это лучше всего? Потоки? Задачи? Является ли while(true) допустимой вещью? Как мне взаимодействовать с экземплярами коммуникатора из моей "службы управления"? То, что я ищу, является аннотированным решением типа плиты котла:)
2 ответа
Я бы порекомендовал посмотреть на использование пула потоков. Это поможет в управлении ресурсами и позволит более эффективно использовать ресурсы.
Что касается завершающих потоков, потоки пула потоков являются фоновыми работниками и будут прерваны, когда ваша служба остановится, однако из приведенного выше описания этого недостаточно. Ваши темы всегда должны иметь возможность получать сообщения с просьбой прекратить их.
Я бы предложил использовать службу веб-API ASP.NET и пометить действия контроллера как асинхронные. С этого момента, используйте Задачи как можно больше, и когда вы закончите с блокировкой ввода-вывода, HTTP-сервер не будет заблокирован. Лично я буду избегать потоков, пока вы не будете абсолютно уверены, что не сможете добиться того же с помощью задач.