Проблема синхронизации потоков, 3 потока выполняются одновременно, 4-й должен работать, пока другие ждут

Извините за заголовок, я не мог найти лучшего объяснения моей проблемы...

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

По сути, у меня есть процесс .Net, который работает в нескольких потоках (все в одном процессе, поэтому нет необходимости в IPC). У меня есть 4 темы:

  • 1 поток, скажем, он называется SpecificThread. Есть System.Timers.Timer, который периодически выполняет какой-то код.
  • 3 других потока, каждый из которых запускает службу, которая периодически выполняет некоторый код (цикл while (true) + Thread.Sleep(few ms)).

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

Итак, у меня есть SpecificThread, который периодически выполняет код. Когда SpecificThread хочет периодически выполнять свой код, он должен ждать, пока другие службы завершат свою задачу. Когда все остальные 3 службы завершили свою задачу, она должна выполнить свой SpecificCode, в то время как другие 3 службы заблокированы. Когда его SpecificCode выполняется, другие 3 службы могут снова запустить свой код.

У меня есть общий экземпляр объекта SynchronizationContext, который используется всеми 4 потоками. Я могу использовать его для синхронизации своих потоков:

public class SynchronizationContext
{
    public void StartService1()
    {
        ...
    }   
    public void StopService1()
    {
        ...
    }
    ...

    public void StartSpecificCode()
    {
        // Some sync here that wait until all 3 services completed their
        // respective tasks
    }
    public void NotifySpecificCodeCompleted()
    {
        // Some sync here that allows services 1 to 3 to execute again
    }
}

Механизм выполнения 3 сервисов выглядит так:

// Only exits the loop when stopping the whole .Net process
while (myService.IsRunning)
{
    try
    {
        this.synchronizationContext.StartService1();

        // Do some job
    }
    finally
    {
        this.synchronizationContext.EndService1();

        // Avoids too much CPU usage for nothing in the loop
        Thread.Sleep(50);
    }
}

Механизм выполнения SpecificThread:

// System.Timers.Timer that is instantiated on process start
if (this.timer != null)
{
    this.timer.Stop();
}

try
{
    // Must blocks until computation is possible
    this.synchronizationContext.StartSpecificCode();

    // Some job here that must execute while other 3
    // services are waiting
}
finally
{
    // Notify computation is done
    this.synchronizationContext.NotifySpecificCodeCompleted();

    // Starts timer again
    if (this.timer != null)
    {
        this.timer.Start();
    }
}

Я не могу понять, как использовать критические разделы, так как только SpecificThread должен работать, пока другие ждут. Я не нашел способа ни с Semaphore, ни с AutoResetEvent (их использование привело к трудной для отладки взаимоблокировке в моем коде). У меня заканчиваются идеи... Может быть, Interlocked статические методы помогут?

Последнее слово: мой код должен работать с .Net 3.5, я не могу использовать ни TPL, ни классы CountdownEvent...

Любая помощь приветствуется!


person ken2k    schedule 28.03.2013    source источник
comment
возможно, будет полезно stackoverflow.com/questions/15657637/condition-variables- c-net   -  person Lanorkin    schedule 28.03.2013


Ответы (1)


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

while (true)
{
    try
    {
        lockObject.EnterReadLock();

        //Do stuff
    }
    finally
    {
        lockObject.ExitReadLock();
    }
}

Затем ваш четвертый поток может ввести блокировку write, когда он захочет выполнить свою работу. Принцип работы блокировок чтения/записи заключается в том, что любое количество читателей может удерживать блокировку до тех пор, пока ни один писатель не удерживает блокировку, и одновременно может быть только один писатель, удерживающий блокировку. Это означает, что ни один из трех рабочих процессов не будет блокировать других рабочих процессов, но рабочие процессы будут блокироваться, если запущен четвертый поток, что именно вам нужно.

person Servy    schedule 28.03.2013
comment
Вау, это звучит именно то, что я ищу! Я тестирую это прямо сейчас - person ken2k; 28.03.2013
comment
Ну, это делает работу идеально. Мой класс SynchronizationContext теперь опирается только на класс ReaderWriterLockSlim, который невероятно прост и удобен в сопровождении. Большое спасибо, я просто пытался создать свой собственный класс ReaderWriterLockSlim, вы сэкономили мне часы. Жаль, что я не могу проголосовать за ваш ответ более одного раза! - person ken2k; 28.03.2013