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-сервер не будет заблокирован. Лично я буду избегать потоков, пока вы не будете абсолютно уверены, что не сможете добиться того же с помощью задач.

Другие вопросы по тегам