我最近在一个类似于下面的测试中遇到了一个面试问题,我没有很多使用线程的开发经验,有人可以帮助建议我如何处理这个问题吗?:
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;
}
}
}
以上方法的以下方法线程安全吗?为什么?
public string DequeueOrNull()
{
if (IsEmpty())
return null;
return Dequeue();
}
答案 0 :(得分:9)
在我看来答案是肯定的。
当isEmpty()过程锁定对象时,它会在返回调用后立即释放 - 一个不同的线程可能在调用IsEmpty()和Dequeue()之间调用DequeueOrNull()(此时对象被解锁),从而删除了唯一存在的项目,使得Dequeue()在那时无效。
一个合理的解决方法是将锁定放在DequeueOrNull()中的两个语句中,因此在检查之后但在DeQueue()之前没有其他线程可以调用DeQueue()。
答案 1 :(得分:3)
这不是线程安全的。在标记的行中,可能从另一个线程调用Dequeue方法,因此,随后的Dequeue返回一个错误的值:
public string DequeueOrNull()
{
if (IsEmpty())
return null;
/// << it is possible that the Dequeue is called from another thread here.
return Dequeue();
}
线程安全代码是:
public string DequeueOrNull()
{
lock(_lockObject) {
if (IsEmpty())
return null;
return Dequeue();
}
}
答案 2 :(得分:2)
不,因为_items
的状态可能会在线程安全的IsEmpty()
和线程安全的Dequeue()
调用之间发生变化。
使用以下内容修复它,以确保在整个操作期间锁定_items
:
public string DequeueOrNull()
{
lock (_lockObject)
{
if (IsEmpty())
return null;
return Dequeue();
}
}
注意:根据_lock
的{{3}},您可能希望通过将IsEmpty()
和Dequeue
的内容移动到单独的帮助函数中来避免双重锁定资源