Служба Windows.NET должна использовать STAThread

Я создал службу Windows, которая будет обращаться к некоторым COM-компонентам, таким образом, я отметил [STAThread] к Основной функции. Однако, когда таймер стреляет, он сообщает о MTA и сбое вызовов COM. Как я могу зафиксировать это?

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
    }
}
14
задан Daniel A. White 7 August 2014 в 12:59
поделиться

5 ответов

Службы запускаются системой хостинга службы windows, которая работает с использованием потоков MTA. Вы не можете это контролировать. Вы должны создать новый Thread и установить его ApartmentState на STA, и делать свою работу на этом потоке.

Вот класс, который расширяет ServiceBase, который делает это:

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();
    }
}

Обратите внимание, что этот код на самом деле не останавливает сервис, а останавливает таймер. Может быть еще много работы на нескольких потоках. Например, если ваша работа состояла в выполнении нескольких запросов от большой базы данных, вы можете закончить сбой, потому что у вас слишком много потоков, работающих в одно и то же время.

В такой ситуации, я бы создал определенное количество потоков STA (может быть, в 2 раза больше ядер, чтобы начать с), которые контролируют потоковую безопасную очередь для рабочих элементов. Тиковое событие таймера отвечало бы за загрузку этой очереди по мере необходимости.

Все зависит от того, что вы на самом деле делаете каждые десять секунд, должен ли он быть завершен в следующий раз, когда тикнет таймер, что вы должны делать в этой ситуации, и т.д. и т.п.

.
25
ответ дан 1 December 2019 в 07:51
поделиться

Установка атрибута STAThread не будет работать на сервисе. Это не будет работать так же, как с приложением, поэтому это будет проигнорировано.

Моя рекомендация - вручную сделать отдельный поток для вашего сервиса, установить его состояние квартиры и переместить все в него. Таким образом, вы сможете корректно настроить поток на STA.

Однако, здесь возникнет еще одна проблема - вам придется переделывать работу вашего сервиса. Нельзя просто использовать экземпляр System.Threading.Timer для синхронизации - он работает в отдельном потоке, которым не будет STA. Когда его истекло событие горит, вы будете работать на другой, не-STA поток.

Вместо того, чтобы делать свою работу в событии таймера, вы, вероятно, захотите сделать свою основную работу в потоке, который вы создаете явно. Вы можете иметь сброс события в том потоке, который блокирует, и ваш таймер "установить" его, чтобы позволить вашей логике, чтобы работать в потоке STA.

.
5
ответ дан 1 December 2019 в 07:51
поделиться

, что не может работать в сервисе, поток, вызывающий ваш метод Main(), уже был запущен менеджером сервиса. Необходимо создать отдельный поток, который инициализируется функцией Thread.SetApartmentState() и перекачивать цикл сообщений

.
4
ответ дан 1 December 2019 в 07:51
поделиться

Глядя на аналогичный пример: http://www.aspfree.com/c/a/C-Sharp/Creating-a-Windows-Service-with-C-Sharp-introduction/1/

А что, если Ваш главный...

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

и сдвинуть таймер запуска/остановки из событий...

 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);
    }
0
ответ дан 1 December 2019 в 07:51
поделиться

Сообщается, что он использует STA. Он основан на предложении Уилла и 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
    }
}
0
ответ дан 1 December 2019 в 07:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: