Есть ли класс синхронизации, который гарантирует порядок FIFO в C #? - PullRequest
14 голосов
/ 07 июня 2009

Что это и как использовать?

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

private void InsertBasicVaraibles(object param)
{
            try
            {
                DataTablesMutex.WaitOne();//mutex for my shared resources
                //insert into DB
            }
            catch (Exception ex)
            {
                //Handle
            }
            finally
            {
                DataTablesMutex.ReleaseMutex();
            }
}

Но в настоящее время мьютекс не гарантирует какой-либо порядок. Там нет ответа после того, как я поставил подробный вопрос !!!

Ответы [ 6 ]

30 голосов
/ 07 июня 2009

Для этого вам нужно написать собственный класс, я нашел этот пример (вставлен, потому что выглядит так, как будто домен сайта истек):

using System.Threading;

public sealed class QueuedLock
{
    private object innerLock;
    private volatile int ticketsCount = 0;
    private volatile int ticketToRide = 1;

    public QueuedLock()
    {
        innerLock = new Object();
    }

    public void Enter()
    {
        int myTicket = Interlocked.Increment(ref ticketsCount);
        Monitor.Enter(innerLock);
        while (true)
        {

            if (myTicket == ticketToRide)
            {
                return;
            }
            else
            {
                Monitor.Wait(innerLock);
            }
        }
    }

    public void Exit()
    {
        Interlocked.Increment(ref ticketToRide);
        Monitor.PulseAll(innerLock);
        Monitor.Exit(innerLock);
    }
}

Пример использования:

QueuedLock queuedLock = new QueuedLock();

try
{
   queuedLock.Enter();
   // here code which needs to be synchronized
   // in correct order
}
finally
{
    queuedLock.Exit();
}

Источник через кеш Google

11 голосов
/ 07 июня 2009

Просто читая «Параллельное программирование в Windows» Джо Даффи, звучит так, как будто вы обычно получаете поведение FIFO от мониторов .NET, но в некоторых ситуациях этого не происходит.

Стр. 273 книги гласит: «Поскольку мониторы используют объекты ядра внутри, они демонстрируют такое же поведение примерно FIFO, как и механизмы синхронизации ОС (описанные в предыдущей главе). Мониторы несправедливы, поэтому, если другой поток проскользнет». и получает блокировку до того, как пробужденный ожидающий поток попытается получить блокировку, скрытному потоку разрешено получить блокировку. "

Я не могу сразу найти раздел, на который ссылается «в предыдущей главе», но он отмечает , что блокировки были сделаны намеренно несправедливыми в последних выпусках Windows, чтобы улучшить масштабируемость и уменьшить число блокировок .

Вам определенно нужен ваш замок, чтобы быть FIFO? Может быть, есть другой способ решения проблемы. Я не знаю ни одной блокировки в .NET, которая гарантированно будет FIFO.

7 голосов
/ 08 июня 2009

Вы должны перепроектировать свою систему, чтобы не полагаться на порядок выполнения потоков. Например, вместо того, чтобы ваши потоки выполняли вызов БД, который может занять более одной секунды, пусть ваши потоки помещают команду, которую они будут выполнять, в структуру данных, такую ​​как очередь (или кучу, если есть что-то, что говорит: быть перед другим "). Затем, в свободное время, опустошите очередь и выполните вставку вашей БД по одной в правильном порядке.

1 голос
/ 27 марта 2017

Ответьте на ответ Мэтью Бриндли.

При преобразовании кода из

lock (LocalConnection.locker) {...}

тогда вы можете сделать IDisposable или сделать то, что я сделал:

public static void Locking(Action action) {
  Lock();
  try {
    action();
  } finally {
    Unlock();
  }
}

LocalConnection.Locking( () => {...});

Я отказался от IDisposable, потому что он создает новый невидимый объект при каждом вызове.

Что касается вопроса повторного входа, я изменил код так:

public sealed class QueuedLock {
    private object innerLock = new object();
    private volatile int ticketsCount = 0;
    private volatile int ticketToRide = 1;
    ThreadLocal<int> reenter = new ThreadLocal<int>();

    public void Enter() {
        reenter.Value++;
        if ( reenter.Value > 1 ) 
            return;
        int myTicket = Interlocked.Increment( ref ticketsCount );
        Monitor.Enter( innerLock );
        while ( true ) {
            if ( myTicket == ticketToRide ) {
                return;
            } else {
                Monitor.Wait( innerLock );
            }
        }
    }

    public void Exit() {
        if ( reenter.Value > 0 ) 
            reenter.Value--;
        if ( reenter.Value > 0 ) 
            return;
        Interlocked.Increment( ref ticketToRide );
        Monitor.PulseAll( innerLock );
        Monitor.Exit( innerLock );
    }
}
1 голос
/ 16 июня 2009

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

    private void InsertBasicVaraibles()
    {
         int functionStopwatch = 0;
         while(true)
         {

           try
           {
             functionStopwatch = Environment.TickCount;
             DataTablesMutex.WaitOne();//mutex for my shared resources
             //insert into DB 
           }
           catch (Exception ex)
           {
             //Handle            
           }
           finally            
           {                
              DataTablesMutex.ReleaseMutex();
           }

           //simulate the timer tick value
           functionStopwatch = Environment.TickCount - functionStopwatch;
           int diff = INSERTION_PERIOD - functionStopwatch;
           int sleep = diff >= 0 ?  diff:0;
           Thread.Sleep(sleep);
        }
    }
1 голос
/ 07 июня 2009

Нет гарантийного порядка для любых встроенных объектов синхронизации: http://msdn.microsoft.com/en-us/library/ms684266(VS.85).aspx

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...