线程安全实施职业测试

时间:2016-01-22 15:36:42

标签: c# multithreading thread-safety

在测试期间,人们问了我一个问题,但我的答案是错误的(不是我的观点)。你有什么意见?提前谢谢!

我们有以下线程安全实现的String Queue类。它真的是线程安全的,类中没有捕获

public class StringQueue
{
    private object _lockObject = new object();

    private List<string> _items = new List<string>();

    public bool IsEmpty()
    {
        lock (_lockObject)
            return _items.Count == 0;
    }

    public void Enqueue(string item)
    {
        lock (_lockObject)
            _items.Add(item);
    }

    public string Dequeue()
    {
        lock (_lockObject)
        {
            string result = _items[0];
            _items.RemoveAt(0);

            return result;
        }
    }
}

这个类的问题是如果队列为空,它会在Dequeue操作上抛出异常。因此,一位软件开发人员决定向该类添加以下函数,如果Queue为空,则返回null:

public string DequeueOrNull()
{
    if (IsEmpty())
        return null;

    return Dequeue();
}

这个函数DequeueOrNull()线程是否安全?

我的回答

是的,这个功能是安全的。该函数只调用另外两个安全函数,然后根据定义它仍然是安全的。解释是,对于新版本的C#,当你检查时队列是空的然后你返回null; C#跳过最后一个返回,然后你没有收到任何错误。使用旧版本的C#,最后一次返回是一个问题,然后你可以用这种方式解决它:

public string DequeueOrNull()
{
    if (IsEmpty())
        return null;
    else
        return Dequeue();
}

2 个答案:

答案 0 :(得分:4)

方法:

public string DequeueOrNull()
{
    if (IsEmpty())
        return null;

    return Dequeue();
}

绝对不是线程安全的(从某种意义上说,你仍然可以获得一个你试图避免的异常)。实际上,这是线程中的常见陷阱。

在这种情况下,它就像是说“好吧,没有其他人触摸过这个。你现在是空的吗?好吧,现在其他所有人都可以触摸它了。” “好吧,没有其他人接触过这个。我知道它不是空的。现在,给我你的东西。好的,其他人都可以触摸它。”

问题在于,在检查它是否为空并从队列中获取项目之间,您已经让其他人访问该对象,因此您检索到的信息可能是“陈旧的”。这是一个相当经典的竞争条件。

旁注:我不会使用使用数组作为队列支持源的集合,但这不是你面试的重点。

奖励积分: 通常,当您对具有多个线程的对象进行操作时,您必须非常小心地管理该对象的状态。

如果有多个线程访问对象可能导致容易出错的状态(比如它使用List<T>作为后备存储),那么与对象交互的最简单方法是序列化所有请求,但是,您仍然需要注意不要对先前的信息采取行动,如果它是您用来确定它是否会导致错误的信息。如果您正在获取信息以防止出现错误,然后对其进行操作(并假设不会出现错误),那么它们都需要在单个 lock语句中发生。

额外奖励积分: 在C#中,locks可以由同一个线程重新输入。所以,你可能能够通过简单地将DequeueOrNull()方法包装在另一个锁语句中来解决这个问题,如下所示:

public string DequeueOrNull()
{
    lock(_lockObject)
    {
        if (IsEmpty())
            return null;

        return Dequeue();
    }
}

我会把它留给比我更聪明的人,以确定这是否会导致死锁。

答案 1 :(得分:0)

我认为你在这里混淆了两个问题:

  • 是线程安全吗?
  • 是否会抛出异常?

您的DequeueOrNull方法线程安全在某种意义上说您不会弄乱您的数据,因为您没有在该方法中写入列表。

但是,当第一个线程刚刚检查到队列是时,当另一个线程的出队速度比当前线程更快并且出队时,你不能避免异常 em>不是空的。