【问题标题】:Concurrent readers in .NET 4.0 C#.NET 4.0 C# 中的并发阅读器
【发布时间】:2011-11-12 03:58:17
【问题描述】:

我在一次编程竞赛中遇到了这个难题,我不应该使用任何内置的并发 .NET 4.0 数据结构。

我必须重写ToString() 方法,并且该方法应该支持并发阅读器。

这是我想出的解决方案,但我坚信它不支持并发阅读器。如何在不锁定列表的情况下支持并发阅读器?

class Puzzle
{
    private List<string> listOfStrings = new List<string>();

    public void Add(string item)
    {
        lock (listOfStrings)
        {
            if (item != null)
            {
                listOfStrings.Add(item);
            }
        }
    }

    public override string ToString()
    {
        lock (listOfStrings)
        {
            return string.Join(",", listOfStrings);
        }
    }
}

【问题讨论】:

  • “并发读者”是指多个线程同时调用.ToString
  • @KirkWoll 是的。我想支持多个线程调用 Tostring()

标签: c# .net concurrency


【解决方案1】:

因为List&lt;T&gt; 只是底层的T[],所以从多个线程读取列表没有什么不安全的。你的问题是边写边读同时写是不安全的。因此,您应该使用ReaderWriterLockSlim

class Puzzle
{
    private List<string> listOfStrings = new List<string>();
    private ReaderWriterLockSlim listLock = new ReaderWriterLockSlim();

    public void Add(string item)
    {
        listLock.EnterWriteLock();

        try
        {
            listOfStrings.Add(item);
        }
        finally
        {
            listlock.ExitWriteLock();
        }
    }

    public override string ToString()
    {
        listLock.EnterReadLock();

        try
        {
            return string.Join(",", listOfStrings);
        }
        finally
        {
            listLock.ExitReadLock();
        }
    }
}

ReaderWriterLockSlim 将允许多个线程在读模式下进入锁,但如果/当有东西在写模式下处于锁中时,它们都会阻塞。同样,在写模式下进入锁将阻塞,直到所有线程都在读模式下退出锁。实际的结果是,只要什么都没有写入,就可以同时发生多个读取,只要没有读取,就可以一次发生一个写入操作。

【讨论】:

  • 你打错了:是ReaderWriterLockSlim(不是ReadWriterLockSlim)。
  • @BrankoDimitrijevic:谢谢!已更正。
【解决方案2】:

既然看起来不需要移除,为什么不能只返回一个字符串?

class Puzzle
{
    private string Value { get; set; }

    public Puzzle()
    {
        Value = String.Empty;
    }
    public void Add(String item)
    {
        Value += "," + item;
    }

    public override string ToString()
    {
        return Value;
    }
}

【讨论】:

  • 这不安全;两个线程同时调用Add 将设置Value 等于其先前的值加上线程值的一个,而不是两者。你也会得到“,Value1,Value2等”。作为您的输出(前导逗号),但这是实现的问题,而不是方法的问题。
  • 也就是说,虽然它不是特别高效(大量的字符串复制等),但如果 OP 不必考虑并发写入者的情况,那么修复前导逗号问题可能仍能解决问题。
  • @Adam: 你可以通过使用StringBuilder来避免字符串连接效率低下。不过,您仍然需要防止多个同时写入。
  • @JimMischel:Daryl 答案的美妙之处在于它的简单性;如果您将其与另一个构造复杂化,那么它将归结为问题定义(未提供)以确定StringBuilderList 是否更好。 FWIW,我对StringBuilder 的线程安全一无所知(例如,同时调用ToString() 是否安全?),而我知道读取List 是安全的。
  • @AdamRobinson 是的,问题集没有提到支持并发作者,只是并发读者。我对 StringBuilder 也有同样的想法,但不知道它是否是线程安全的。所以,不要在企业中使用它,但它会解决所描述的“难题”。
猜你喜欢
  • 2017-05-27
  • 1970-01-01
  • 2010-10-28
  • 2014-09-09
  • 1970-01-01
  • 2012-04-20
  • 1970-01-01
  • 1970-01-01
  • 2016-02-14
相关资源
最近更新 更多