【问题标题】:C# Nested SynchronizationC# 嵌套同步
【发布时间】:2010-01-22 12:57:02
【问题描述】:

我有一个包含多个数据成员的类:

public class Foo
{
    List<int> intLst;
    int Whatever;
    HashTable<string> hshTable;
    ...
}

然后我在 Foo 中有一个函数,它接受两个 Foo 并将它们合并为一个新的 Foo。我也想让 Foo 类线程安全。下面的代码是最好的方法吗?

public Foo(Foo f)
{
    lock(f)
    {
        foreach (int i in f.intLst)
        {
            this.intLst.Add(i)
        }
        //More complicated
    }
}

public Foo Merge(Foo f1, Foo f2)
{
    Foo fReturn = new Foo(f1);//Using the constructor above

    lock(f2)
    {
        foreach (int i in f2.intLst)
        {
            fReturn.intLst.Add(i);
        }

        //More complicated merge code
    }

    return fReturn;
} 

就这么简单吗?我应该改用监视器吗?我知道 lock 实现了监视器类,但也许还有一个原因。我还需要担心在上面的示例中处理死锁。

我觉得我需要 lock() f1 和 f2,因为如果 f2 在 f1 上的复制构造函数期间发生变化怎么办?

【问题讨论】:

  • 顺便说一句,List 类型有 Union() 和 Concat() 方法...

标签: c# multithreading


【解决方案1】:

是的,就是这么简单。

你的代码很好。在访问 f2 之前,您不需要锁定它,在复制 f1 之后,您也不需要锁定它。所以真的没有必要同时锁定两者。

在您的示例中,项目本身是不可变的简单整数,因此无需锁定它们,但如果您有可变项目,则可能需要锁定单个项目。

您无需担心仅使用此代码会出现死锁,因为您一次只持有一个锁。当然,如果其他代码在持有锁时调用它,您可能需要担心死锁的可能性。

【讨论】:

  • 在我的实际应用程序中,项目是可变的。一旦我在 Merge() 中调用了复制构造函数,我相信 f2 会在锁定之前发生变化吗?这个对吗。我想确保调用 Merge 函数后,f1 和 f2 都不能更改。我可能只是需要一些安慰。
  • 您需要考虑的场景如下: 1. 您的代码复制 f1 然后释放锁。 2. 另一个线程对 f1 和 f2 进行了一次更改。 3. 你的代码复制 f2 然后释放锁。在这种情况下,您的代码将拥有另一个线程更改之前的 f1 副本和之后的 f2 副本。在您的特定应用程序中,这会是一件坏事吗?如果答案是肯定的,您需要额外的锁定。如果答案是否定的,那么你不会。一般来说,如果两者都是单个结构的一部分,则答案是肯定的,在这种情况下,您可能应该锁定该结构。
【解决方案2】:

lock() 仅适用于其他 lock() 块。所以 lock(f) 将阻塞其他与 f 一起工作的代码,只要该代码也有 lock(f),所以 lock(f) 必须在 (f) 的所有用法上。

您无需担心锁定目标 Foo,因为它仍在构建中 - 没有其他东西可以引用它。

【讨论】:

    猜你喜欢
    • 2012-05-09
    • 1970-01-01
    • 1970-01-01
    • 2012-06-05
    • 2018-11-01
    • 2011-08-18
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多