我正在尝试阻止多线程服务器中的数据争用。我的问题如下:有一个List<RServer>,类型RServer是一个包含多个字段的类。现在,服务器有几个线程全部同时运行,他们可以修改List(添加更多项)和单个readonly object RServerLock = new object( )实例(更改字段)。

所以我的策略是在每个readonly object RServerListLock = new object( )实例中创建一个lock,另外一个RServerLock并将所有修改的代码(<=>或<=>实例)包含在< =>。这样安全吗?如果某个线程试图锁定<=> 另一个锁定它时会发生什么?

有帮助吗?

解决方案

如果你有一个竞争锁,第二个线程必须等到第一个发布锁。

您的计划听起来几乎没问题 - 但您还需要在读取数据时锁定,以确保获得最新值和一致值。否则你可能会在一个线程中编写一些值,并在一个不同的线程中同时看到一些新值 - 但可能不是全部 - 以及旧值。

如果你能尽可能避免这样做,你的生活会更容易:)不可变的类型使线程变得更加简单。

不要忘记,如果您的代码需要同时需要两个锁(例如,添加一个RServer并以原子方式修改另一个),必须确保始终获取锁定相同的顺序 - 如果一个线程在持有锁A时尝试获取锁B,而另一个线程在持有锁B时尝试获取锁A,则最终会出现死锁。

请参阅我的线程教程 Joe Albahari的了解更多细节。此外,如果您对并发感兴趣,Joe Duffy有一个优秀书籍很快就会出来。

其他提示

看起来你有一个ReaderWriterLock的主要候选人。最好使用的类(如果运行时支持它,我认为3.0+)是ReaderWriterLockSlim,因为原始的ReaderWriterLock存在性能问题。

其中一位MSDN杂志的作者也遇到了RWLS课程的问题,我不会在这里详细说明,但你可以看看它 here

我知道下面的代码会产生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

将它放在扩展方法类(public static class [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.
}

如果线程试图锁定已经锁定的对象,它将一直阻塞,直到锁定被释放。当两个线程试图同时锁定它时,并不存在并发问题,因为锁是一个原子操作,因此其中一个线程将永远是锁的受害者并最终阻塞。

如果您需要完整锁定RServer实例,那么您的策略听起来很合理。如果您可以专门锁定特定的RServer实例字段,那可能会更有效。但是它会增加锁定操作的次数并且会更复杂。

这很安全。如果一个线程获得了锁定,则其他线程必须等到锁定被释放。

然而,由于锁定可能是全局性的,因此不太可能出现性能问题。这实际上取决于你的状态以及它们如何被这些线程变异,所以我无法帮助你。

重述一点 - 每个RServer实例都有一个名为RServerLock的变量,它将使用锁定块锁定。

线程1(T1)锁定RServer 1(R1)。线程2(T2)尝试修改R1,导致输入R1锁定块。在这种情况下,T2将等到T1结束。

要特别注意的一件事是你最终会有多少个RServer实例。如果你最终得到了大量的实例,那么你只需携带20个字节的数据来锁定。此时,您可能需要考虑锁定条带化。

如果我正确地理解你的问题,听起来你正试图创造一个已经存在完美好车轮的车轮。

查看MSDN中的system.threading命名空间。它有许多专门用于处理这种情况的锁定机制。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top