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.
- Ho già capito che
while(true)
conSleep
all'interno non è probabilmente la scelta più intelligente. Ci sono stato, l'ho fatto .. - 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.