Синхронизация контекстов в сервисе

215
23 июля 2017, 08:20

Есть сервис

class MainService : ServiceBase 
{
  event EventHandler MyEvent;
  Thread m_Thread = null;
  ...
  protected override void OnStart(string[] args)
  { 
    //тут System.Threading.Thread.CurrentThread.ManagedThreadId = 1
    MyEvent += delegate { 
      //Тут System.Threading.Thread.CurrentThread.ManagedThreadId = 5
    };
    m_Thread = new Thread(new ThreadStart(MonitorThread));
    m_Thread.IsBackground = true;
    m_Thread.Start();
  }
  protected override void OnStop()
  {
    m_Thread.Abort();
  }
  ...
  private void MonitorThread()
  {
    //Something
    MyEvent?.Invoke(this, EventArg.Empty);
    //Something
  }
  ...
}

в MonitorThread иногда вызываются события MyEvent, естественно вызов происходит в потоке отличного от основного потока, как мне вызвать событие или какой-либо метод по событию в основном потоке???

PS: SynchronizationContext.Current в сервисе = null

PS2: Сборка в .Net Framework 2.0

Answer 1

Никак, основной поток в вин-службах полностью отведен под управление временем жизни и системные события (OnStart, OnStop и т.п.)

Если вам зачем-то нужен основной поток - надо просто выбрать один из ваших дополнительных потоков и "назначить" его основным.

Если вас не устраивает ваш MonitorThread в качестве такового - держите реализацию потока с очередью сообщений:

class QueueSynchronizationContext : SynchronizationContext, IDisposable
{
    private readonly BlockingCollection<WorkItem> queue = new BlockingCollection<WorkItem>();
    public override void Post(SendOrPostCallback d, object state)
    {
        try { queue.Add(new WorkItem(d, state)); }
        catch (InvalidOperationException) { if (Debugger.IsAttached) Debugger.Break(); }
        // Если уже был вызван CompleteAdding - мы уже не можем добавить элемент в очередь, но и позволять службе падать также не стоит
        // (нет ничего глупее чем программа которая падает при закрытии). Поэтому лучшее что тут можно сделать - это забыть.
    }
    public override void Send(SendOrPostCallback d, object state)
    {
        try
        {
            using (var e = new ManualResetEvent(initialState: false))
            {
                queue.Add(new WorkItem(d, state, e));
                e.WaitOne();
            }
        }
        catch (InvalidOperationException) { if (Debugger.IsAttached) Debugger.Break(); }
        // Если уже был вызван CompleteAdding - мы уже не можем добавить элемент в очередь, но и позволять службе падать также не стоит
        // (нет ничего глупее чем программа которая падает при закрытии). Поэтому лучшее что тут можно сделать - это забыть.
    }
    public bool ProcessQueue(CancellationToken token)
    {
        try
        {
            foreach (var wi in queue.GetConsumingEnumerable(token))
                wi.Execute();
            return true;
        }
        catch (OperationCanceledException) { return false; }
    }
    public void Complete() => queue.CompleteAdding();
    public void Dispose() => queue.Dispose();
    private class WorkItem
    {
        private SendOrPostCallback d;
        private object state;
        private ManualResetEvent e;
        public WorkItem(SendOrPostCallback d, object state, ManualResetEvent e = null)
        {
            this.d = d;
            this.state = state;
            this.e = e;
        }
        public void Execute()
        {
            try { d(state); }
            finally { e?.Set(); }
        }
    }
}
class MyService : ServiceBase
{
    private QueueSynchronizationContext ctx;
    private Thread mainThread;
    protected override void OnStart(string[] args)
    {
        this.ctx = new QueueSynchronizationContext();
        this.mainThread = new Thread(MainThread);
        this.mainThread.Start();
    }
    protected override void OnStop()
    {
        // Не забыть отменить фоновые задачи и дождаться их завершения 
        // ПЕРЕД вызовом ctx.Complete()!
        ctx.Complete();
        mainThread.Join();
        ctx.Dispose();
    }
    private void MainThread()
    {
        SynchronizationContext.SetSynchronizationContext(ctx);
        ctx.ProcessQueue(CancellationToken.None);
    }
}
READ ALSO
Архитектура классов

Архитектура классов

Есть примерно следующая инфраструктура классов:

351
Влияют ли пустые переменные на время работы

Влияют ли пустые переменные на время работы

Вот мне стало интересно, влияют ли переменные пустые на работу кода?

267
Как вывести первый элемент из коллекции ASP.NET

Как вывести первый элемент из коллекции ASP.NET

Есть модель новостей, там коллекция картинок (их несколько штук для каждой новости)Надо вывести только первую картинку из этой коллекции

265