C # /. NET design dell'applicazione multithread

1

L'applicazione da progettare funge da ponte tra due sistemi diversi.
Si parla nativamente di TCP (in realtà RS232, ma c'è un server di ETH COM nella linea di comunicazione) - l'altro è un sistema ERP (in grado di comunicare attraverso i servizi Web).

Quindi in una foto sembrerebbe che:

|| dispositivo || < ---- TCP ---- > || la mia applicazione || < ---- ---- WCF > || ERP ||

Ora come sviluppatore web non avevo mai avuto molto a che fare con i thread (cambiato un po 'con MVC e Task ma ancora ...).

Ora sono sfidato con un design "server" adeguato.

  1. Ho già capito che while(true) con Sleep all'interno non è probabilmente la scelta più intelligente. Ci sono stato, l'ho fatto ..
  2. Da quanto ho letto finora ho bisogno di qualcosa che blocchi. Come Console.ReadKey () solo che non voglio eseguire questo come un'applicazione console. Dovrebbe essere eseguito come servizio in background (ad esempio Servizio Windows).

Ho dato uno sguardo al modello consumatore / produttore usando un BlockingCollection che è abbastanza accurato. Ora ho il mio produttore (server WCF) che attiva la creazione dei consumatori.

La mia domanda ora è ... come faccio a - dal mio host di WCF (= produttore) - accedere alle mie istanze multiple di consumatori? Devo interrogare il loro stato interno e inoltre devo essere in grado di distruggere le istanze di singoli consumatori se si bloccano - succede.

OperationsManager:

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using WindowsService2.Producer;

namespace WindowsService2
{
    internal class OperationsManager
    {
        private CancellationTokenSource _cts;
        private List<Task> _tasks;
        private ILogger _logger;

        internal OperationsManager()
        {
            _cts = new CancellationTokenSource();
            _tasks = new List<Task>();
            _logger = new Logger("OperationsManager");
        }

        //internal int ConsumerCount { get; set; }

        internal void Start()
        {
            _logger.Log("Start() called.");

            StartProducer();
            StartConsumers();
        }

        private void StartConsumers()
        {
            // NOP.
        }

        private void StartProducer()
        {
            BlockingCollection<ProducerMessage> blockingCollection = new BlockingCollection<ProducerMessage>();

            Task producerTask = Task.Factory.StartNew(() =>
            {
                ManagementService managementService = new ManagementService(blockingCollection, _cts.Token);
                managementService.Produce();
            }, _cts.Token);

            _tasks.Add(producerTask);
        }

        internal void Stop()
        {
            _logger.Log("Stop() called.");

            _cts.Cancel();
            Task.WaitAll(_tasks.ToArray(), Timeout.Infinite);

            _cts.Dispose();
        }
    }
}

Produttore

using System;
using System.Collections.Concurrent;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Threading;
using WindowsService2.Producer.WCF;

namespace WindowsService2.Producer
{
    // this is my "producer"
    internal class ManagementService : Producer
    {
        private Logger _logger;
        private ServiceHost _serviceHost;

        internal ManagementService(BlockingCollection<ProducerMessage> blockingCollection, CancellationToken cancellationToken) 
            : base(blockingCollection, cancellationToken)
        {
            _logger = new Logger("ManagementService");
            _cancellationToken.Register(ShutDownWcfEndpoint);
        }

        public override void Produce()
        {
            _logger.Log("Produce() called.");

            StartWcfEndpoint();
        }

        private void StartWcfEndpoint()
        {
            // see https://msdn.microsoft.com/en-us/library/ms731758%28v=vs.110%29.aspx
            Uri baseAddress = new Uri("http://localhost:8080/hello");

            // Create the ServiceHost.
            _serviceHost = new ServiceHost(typeof(HelloWorldService), baseAddress);

            // Enable metadata publishing.
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            _serviceHost.Description.Behaviors.Add(smb);

            // Open the ServiceHost to start listening for messages. Since
            // no endpoints are explicitly configured, the runtime will create
            // one endpoint per base address for each service contract implemented
            // by the service.
            _serviceHost.Open();
        }

        private void ShutDownWcfEndpoint()
        {
            _logger.Log("Shutting down WCF endpoint...");
            _serviceHost.Close();

            _logger.Log("Shutting down WCF endpoint... completed.");
        }
    }
}

dei consumatori

public class Consumer
{
    // No code yet.
    // It has an "internal state" and uses TcpListener to communicate with the device.
    // It should be able to take commands from the management service (producer) 
    // (like: destroy yourself, what's your status?, ...)
}

Come puoi vedere, non ci sono ancora consumatori creati. Questo perché ho pensato che il design potrebbe non funzionare, dopotutto.

  • Il servizio di gestione (produttore) deve innescare nuovi consumatori. Quindi il servizio di gestione deve scrivere in BlockingCollection - ma come? statico pubblico BlockingCollection?
  • Il servizio di gestione dovrebbe fornire un metodo che mostri lo "stato interno" di ciascun consumatore = > quanti consumatori ci sono anche?

Penso che lo schema produttore / consumatore potrebbe essere la scelta sbagliata qui. Quello di cui ho bisogno è "Hey sono il servizio di gestione e ho il numero x di" thread di lavoro "e il loro stato è collection[0].State.

Come lo faresti?

Prima che questa domanda venga chiusa come troppo ampia - di solito accade con questo tipo di domanda - almeno su SO - per favore forniscimi qualche consiglio su come suddividere i requisiti in pezzi più piccoli e più gestibili.

    
posta lapsus 24.04.2015 - 21:06
fonte

1 risposta

1

Penso che tu abbia una buona idea di andare qui.

I had a look at the consumer/producer pattern using a BlockingCollection which is pretty neat. I now have my producer (WCF server) which triggers creation of consumers.

L'equivalente server back-end del modello consumatore / produttore è un modello di comunicazione asincrono. Ciò non significa che deve essere lento o poco frequente. Ma piuttosto che dormire, bloccare i thread ecc. Quando la condizione "Completo" è soddisfatta, aspetti che il cliente dica, invia di nuovo ora.

My question now is... how do I - from my WCF self host (=producer) - access my multiple consumer instances? I need to query their internal state and also I need to be able to destroy single consumer instances if they become stuck - it happens.

I pattern asincroni sono ora usati in vari modelli. Uno interessante che ha alcuni aspetti che potresti utilizzare è la registrazione.

Se il produttore genera i client, dovrebbe conoscere tutti gli ID dei clienti. Potrei quindi teoricamente chiamare, sei sveglio? Nessuna risposta, ucciderlo (usando la chiamata al sistema operativo?) E poi respawn. A rischio di una perdita di dati.

Un esempio di come funziona questo tipo di pattern è SignalR (meno la parte Kill) Panoramica SignalR con ulteriori informazioni

Ma penso che fornirà un esempio sufficiente per modellare qualcosa di simile al tuo scenario.

    
risposta data 25.04.2015 - 21:50
fonte

Leggi altre domande sui tag