我正在尝试调查锁定以创建线程安全类并且有几个问题。鉴于以下课程:
public class StringMe
{
protected ArrayList _stringArrayList = new ArrayList();
static readonly object _locker = new object();
public void AddString(string stringToAdd)
{
lock (_locker) _stringArrayList.Add(stringToAdd);
}
public override string ToString()
{
lock (_locker)
{
return string.Join(",",string[])_stringArrayList.ToArray(Type.GetType("System.String")));
}
}
}
1)我是否成功制作了AddString和TMString threadsafe?
2)在我创建的ToString方法中是否需要锁定它以使其线程安全?
3)是否只需要修改需要锁定数据的方法,或者是否需要锁定读取和写入操作以使其线程安全?
非常感谢您的时间!
答案 0 :(得分:13)
不,您没有使这些调用成为线程安全的 - 因为_stringArrayList
字段受到保护。在调用AddString
和ToString
时,子类可以随意执行任何操作。
例如(正如其他答案声称您的代码 线程安全。)
public class BadStringMe : StringMe
{
public void FurtleWithList()
{
while (true)
{
_stringArrayList.Add("Eek!");
_stringArrayList.Clear();
}
}
}
然后:
BadStringMe bad = new BadStringMe();
new Thread(bad.FurtleWithList).Start();
bad.AddString("This isn't thread-safe");
首选私有字段 - 这样可以更轻松地推理您的代码。
此外:
List<T>
到ArrayList
StringMe
个实例,只有一个线程可以在{{1}中} 总计 AddString
比typeof(string)
3)是否只需要修改需要锁定数据的方法,或者是否需要锁定读取和写入操作以使其线程安全?
全部,假设可能有一些操作。如果所有只是读取,则不需要任何锁定 - 但是否则您的读取线程可以读取数据结构中的两位数据,这些数据在两者之间进行了修改,即使只有一个写线程。 (还要考虑记忆模型的考虑因素。)
答案 1 :(得分:4)
1)我是否成功制作了AddString和TMString threadsafe?
是的,如果您将_stringArrayList
更改为私人
2)在我创建的ToString方法中是否有必要锁定它以使其线程安全?
是
3)是否只需要修改需要锁定数据的方法,或者是否需要锁定读取和写入操作以使其线程安全?
读写。
答案 2 :(得分:3)
对所有三个都是肯定的(即读/写到最后一个)。
但还有更多:
您创建锁定对象static
,而您保护的数据是每个实例字段。这意味着StringMe
的所有实例都相互受到保护,尽管它们具有不同的数据(即_stringArrayList
的实例)。对于您提供的示例,您可以从static
中删除_locker
修饰符。更确切地说,您通常为一组数据定义“锁定”,或者更好的不变量,您要保留。通常,锁的生命周期(和范围)应该等于数据的生命周期(和范围)。
另外,为了更好地衡量,您保护的数据的可见性不应高于锁定数据。在您的示例中,派生实现可以在不获取锁定的情况下更改_stringArrayList
(因为它受到保护),从而破坏了不变量。我会同时使它们private
,如果必须的话,只将_stringArrayList
通过(正确锁定)方法暴露给派生类。