Domanda

Ho creato un servizio di Windows che verrà chiamando ad alcuni componenti COM, così ho etichettato [STAThread] per la funzione principale. Tuttavia, quando i timer incendi, riporta MTA e le chiamate COM sicuro. Come posso risolvere questo problema?

using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
using System.Timers;



namespace MyMonitorService
{
    public class MyMonitor : ServiceBase
    {
        #region Members
        private System.Timers.Timer timer = new System.Timers.Timer();
        #endregion

        #region Construction
        public MyMonitor ()
        {
            this.timer.Interval = 10000; // set for 10 seconds
            this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.timer_Elapsed);
        }
        #endregion

        private void timer_Elapsed (object sender, ElapsedEventArgs e)
        {
            EventLog.WriteEntry("MyMonitor", String.Format("Thread Model: {0}", Thread.CurrentThread.GetApartmentState().ToString()), EventLogEntryType.Information);
        }

        #region Service Start/Stop
        [STAThread]
        public static void Main ()
        {
            ServiceBase.Run(new MyMonitor());
        }

        protected override void OnStart (string[] args)
        {
            EventLog.WriteEntry("MyMonitor", "My Monitor Service Started", EventLogEntryType.Information);
            this.timer.Enabled = true;
        }

        protected override void OnStop ()
        {
            EventLog.WriteEntry("MyMonitor", "My Monitor Service Stopped", EventLogEntryType.Information);
            this.timer.Enabled = false;
        }
        #endregion
    }
}
È stato utile?

Soluzione

I servizi sono gestiti dal sistema di servizio di hosting windows, che corre utilizzando thread MTA. Non è possibile controllare questo. È necessario creare una nuova rel="noreferrer"> Discussione e impostare l'ApartmentState STA , e fare il vostro lavoro su questo thread.

Ecco una classe che estende ServiceBase che fa questo:

public partial class Service1 : ServiceBase
{
    private System.Timers.Timer timer;

    public Service1()
    {
        InitializeComponent();
        timer = new System.Timers.Timer();
        this.timer.Interval = 10000; // set for 10 seconds
        this.timer.Elapsed += new System.Timers.ElapsedEventHandler(Tick);
    }

    protected override void OnStart(string[] args)
    {
        timer.Start();
    }

    private void Tick(object sender, ElapsedEventArgs e)
    {
        // create a thread, give it the worker, let it go
        // is collected when done (not IDisposable)
        var thread = new Thread(WorkerMethod);
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        OnStop(); // kill the timer
    }

    private void WorkerMethod(object state)
    {
        // do your work here in an STA thread
    }

    protected override void OnStop()
    {
        timer.Stop();
        timer.Dispose();
    }
}

Nota questo codice in realtà non interrompere il servizio, si ferma il timer. Ci potrebbe essere un sacco di lavoro ancora in corso per più thread. Per esempio, se il vostro lavoro consisteva di eseguire più query fuori un database di grandi dimensioni si può finire per schiantarsi perché avete troppi thread in esecuzione allo stesso tempo.

In una situazione come questa, mi piacerebbe creare un determinato numero di thread STA (forse 2 volte il numero di core per cominciare con), che monitorare una coda di thread-safe per elementi di lavoro. L'evento timer tick sarebbe responsabile per il caricamento quella coda con il lavoro che necessitano di fatto.

Tutto dipende da ciò che si sta effettivamente facendo ogni dieci secondi, anche se non dovrebbe essere completata la prossima volta che il timer zecche, che cosa si dovrebbe fare in questa situazione, ecc ecc.

Altri suggerimenti

L'impostazione dell'attributo STAThread non funziona su un servizio. Non è gestita allo stesso modo di un programma, quindi questo sarà ottenere ignorato.

Il mio consiglio sarebbe quello di fare manualmente un thread separato per il vostro servizio, impostare il suo stato appartamento, e spostare tutto in esso. In questo modo, è possibile impostare il thread STA correttamente.

Tuttavia, ci sarà un altro problema qui - si dovrà rielaborare il modo in cui il servizio funziona. Non si può semplicemente utilizzare un System.Threading.Timer istanza per temporizzazione - funziona su un thread separato, che non sarà STA. Quando i suoi fuochi di evento trascorsi, lavorerete su un diverso, non STA thread.

Invece di fare il vostro lavoro in caso timer, probabilmente si vorrà fare il vostro lavoro principale nel thread si crea in modo esplicito. Si può avere un evento di reset in quel filo che blocca, e hanno il timer "set" per consentire la logica per eseguire nel thread STA.

Questo non può funzionare in un servizio, il thread che chiama il metodo Main () è stato già avviato dal responsabile del servizio. È necessario creare un thread separato che viene inizializzato con Thread.SetApartmentState () e pompe un ciclo di messaggi.

Guardando un esempio simile: http://www.aspfree.com/c/a/C-Sharp/Creating-a-Windows-Service-with-C-Sharp-introduction/1/

Cosa fare se la principale è ...

    [STAThread]
    public static void Main ()
    {
        MyMonitor m = new MyMonitor();
        m.Start();
    }

e spostare il vostro inizio del timer / stop out degli eventi ...

 public void Start() { this.timer.Enabled = true;}
 public void Stop() { this.timer.Enabled = false;}

  protected override void OnStart (string[] args)
    {
        EventLog.WriteEntry("MyMonitor", "My Monitor Service Started", EventLogEntryType.Information);
    }

    protected override void OnStop ()
    {
        EventLog.WriteEntry("MyMonitor", "My Monitor Service Stopped", EventLogEntryType.Information);
    }

Questo segnala che sta usando STA. Si basa su di Will suggerimento e http: //en.csharp -online.net/Creating_a_.NET_Windows_Service%E2%80%94Alternative_1:_Use_a_Separate_Thread

using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;



namespace MyMonitorService
{
    internal class MyMonitorThreaded : ServiceBase
    {
        private Boolean bServiceStarted = false;
        private Thread threadWorker;

        private void WorkLoop ()
        {
            while (this.bServiceStarted)
            {
                EventLog.WriteEntry("MyMonitor", String.Format("Thread Model: {0}", Thread.CurrentThread.GetApartmentState().ToString()), EventLogEntryType.Information);

                if (this.bServiceStarted)
                    Thread.Sleep(new TimeSpan(0, 0, 10));
            }

            Thread.CurrentThread.Abort();
        }

        #region Service Start/Stop
        protected override void OnStart (String[] args)
        {
            this.threadWorker = new Thread(WorkLoop);
            this.threadWorker.SetApartmentState(ApartmentState.STA);
            this.bServiceStarted = true;
            this.threadWorker.Start();
        }

        protected override void OnStop ()
        {
            this.bServiceStarted = false;
            this.threadWorker.Join(new TimeSpan(0, 2, 0));
        }
        #endregion
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top