блокировка c # - PullRequest
       22

блокировка c #

4 голосов
/ 22 октября 2008

Я пытаюсь предотвратить скачки данных на многопоточном сервере. Моя проблема заключается в следующем: есть List<RServer>, тип RServer это класс с несколькими полями. Теперь сервер имеет несколько потоков, работающих одновременно, и они могут изменять как List (добавление дополнительных элементов), так и отдельные RServer экземпляры (изменение полей).

Таким образом, моя стратегия состоит в том, чтобы создать readonly object RServerLock = new object( ) в каждом из RServer экземпляров и дополнительно readonly object RServerListLock = new object( ) и заключить весь код, который изменяет (экземпляр List или RServer) в lock. Это безопасно? Что произойдет, если поток попытается заблокировать RServerLock , а другой заблокирует его?

Ответы [ 6 ]

8 голосов
/ 22 октября 2008

Если у вас есть состязательная блокировка, второй поток должен ждать, пока первая не снимет блокировку.

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

Если вы сможете избежать этого, насколько это возможно, ваша жизнь станет проще :) Неизменяемые типы значительно упрощают работу с потоками.

Не забывайте, что если у вас когда-нибудь будет код, который будет нуждаться в двух блокировках одновременно (например, добавление одного RServer и изменение другого атомарно), вы должны убедиться, что вы всегда получаете блокировки в тот же порядок - если один поток пытается получить блокировку B, пока он удерживает блокировку A, а другой поток пытается получить блокировку A, пока он удерживает блокировку B, вы получите тупик.

См. Мое учебное пособие по потокам или Джо Албахари для получения более подробной информации. Кроме того, если вас интересует параллелизм, у Джо Даффи есть отличная книга 1020 *, которая скоро выйдет.

4 голосов
/ 22 октября 2008

Похоже, у вас есть главный кандидат на ReaderWriterLock. Лучший класс для использования (если ваша среда выполнения поддерживает его, я думаю, 3.0+) - это ReaderWriterLockSlim, поскольку у исходного ReaderWriterLock есть проблемы с производительностью.

Один из авторов журнала MSDN также столкнулся с проблемой с классом RWLS, я не буду вдаваться в подробности здесь, но вы можете посмотреть его здесь .

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

    /// <summary>
    /// Opens the specified reader writer lock in read mode,
    /// specifying whether or not it may be upgraded.
    /// </summary>
    /// <param name="slim"></param>
    /// <param name="upgradeable"></param>
    /// <returns></returns>
    public static IDisposable Read(this ReaderWriterLockSlim slim, bool upgradeable)
    {
        return new ReaderWriterLockSlimController(slim, true, upgradeable);
    } // IDisposable Read

    /// <summary>
    /// Opens the specified reader writer lock in read mode,
    /// and does not allow upgrading.
    /// </summary>
    /// <param name="slim"></param>
    /// <returns></returns>
    public static IDisposable Read(this ReaderWriterLockSlim slim)
    {
        return new ReaderWriterLockSlimController(slim, true, false);
    } // IDisposable Read

    /// <summary>
    /// Opens the specified reader writer lock in write mode.
    /// </summary>
    /// <param name="slim"></param>
    /// <returns></returns>
    public static IDisposable Write(this ReaderWriterLockSlim slim)
    {
        return new ReaderWriterLockSlimController(slim, false, false);
    } // IDisposable Write

    private class ReaderWriterLockSlimController : IDisposable
    {
        #region Fields

        private bool _closed = false;
        private bool _read = false;
        private ReaderWriterLockSlim _slim;
        private bool _upgrade = false;

        #endregion Fields

        #region Constructors

        public ReaderWriterLockSlimController(ReaderWriterLockSlim slim, bool read, bool upgrade)
        {
            _slim = slim;
            _read = read;
            _upgrade = upgrade;

            if (_read)
            {
                if (upgrade)
                {
                    _slim.EnterUpgradeableReadLock();
                }
                else
                {
                    _slim.EnterReadLock();
                }
            }
            else
            {
                _slim.EnterWriteLock();
            }
        } //  ReaderWriterLockSlimController

        ~ReaderWriterLockSlimController()
        {
            Dispose();
        } //  ~ReaderWriterLockSlimController

        #endregion Constructors

        #region Methods

        public void Dispose()
        {
            if (_closed)
                return;
            _closed = true;

            if (_read)
            {
                if (_upgrade)
                {
                    _slim.ExitUpgradeableReadLock();
                }
                else
                {
                    _slim.ExitReadLock();
                }
            }
            else
            {
                _slim.ExitWriteLock();
            }

            GC.SuppressFinalize(this);
        } // void Dispose

        #endregion Methods
    } // Class ReaderWriterLockSlimController

Поместите это в класс метода расширения (открытый статический класс [Name]) и используйте его следующим образом:

using(myReaderWriterLockSlim.Read())
{
  // Do read operations.
}

Или

using(myReaderWriterLockSlim.Read(true))
{
  // Read a flag.
  if(flag)
  {
    using(myReaderWriterLockSlim.Write()) // Because we said Read(true).
    {
      // Do read/write operations.
    }
  }
}

Или

using(myReaderWriterLockSlim.Write()) // This means you can also safely read.
{
  // Do read/write operations.
}
2 голосов
/ 22 октября 2008

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

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

0 голосов
/ 22 октября 2008

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

Проверьте пространство имен system.threading в MSDN. Он имеет множество механизмов блокировки, разработанных специально для таких ситуаций.

http://msdn.microsoft.com/en-us/library/system.threading.aspx

0 голосов
/ 22 октября 2008

Для краткости - каждый экземпляр RServer имеет переменную с именем RServerLock, которая будет заблокирована с помощью блока блокировки.

Поток 1 (T1) блокирует RServer 1 (R1). Поток 2 (T2) пытается изменить R1, что приводит к вводу блока блокировки R1. В этом случае T2 будет ждать окончания T1.

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

0 голосов
/ 22 октября 2008

Это безопасно. Если один поток получил блокировку, другим потокам придется ждать, пока блокировка не будет снята.

Однако, как бы это ни было невероятно, вы можете столкнуться с проблемой производительности, так как блокировка может быть СЛИШКОМ глобальной. Это действительно зависит от того, каково ваше состояние и как оно мутирует в этих потоках, поэтому я не могу вам с этим помочь.

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