锁定以使类具有C#示例的线程安全性,或者该类是线程安全的
本文关键字:线程 安全性 安全 或者 锁定 | 更新日期: 2023-09-27 18:20:42
我正试图研究锁定以创建线程安全类,并有几个问题。给定以下类别:
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和ToString线程安全?
2) 在我创建的ToString方法中,是否需要将其锁定以确保线程安全?
3) 是否只有修改数据的方法需要锁定,或者执行读取和写入操作的方法都需要锁定以确保线程安全?
非常感谢您抽出时间!
不,您还没有使这些调用成为线程安全的,因为_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
实例,一次也只能有一个线程在AddString
中 - 使用
typeof(string)
比使用Type.GetType("System.String")
干净得多
3) 是否只有修改数据的方法需要锁定,或者执行读取和写入操作的方法都需要锁定以确保线程安全?
所有,假设可能存在一些操作。如果所有都只是读取,则不需要任何锁,但否则,即使只有一个写入线程,您的读取线程也可以从数据结构中读取两位数据,这两位数据在其间进行了修改。(还需要考虑内存模型。)
1) 我是否成功地使AddString和ToString线程安全?
是的,如果您将_stringArrayList
更改为专用
2) 在我创建的ToString方法中,是否需要将其锁定以确保线程安全?
是
3) 是否只有修改数据的方法需要锁定,或者执行读取和写入操作的方法都需要锁定以确保线程安全?
读写。
对所有三个都是(即读/写到最后一个)。
但还有更多:
您将锁定对象设为static
,而保护的数据是每个实例的字段。这意味着StringMe
的所有实例都受到相互保护,尽管它们具有不同的数据(即_stringArrayList
的实例)。对于您给出的示例,可以从_locker
中删除static
修饰符。更准确地说,您通常为一组数据定义一个"锁",或者更好的是,您想要保留的不变量。通常情况下,锁的生存期(和作用域)应该等于数据的生存期。
此外,为了更好地衡量,您对所保护的数据的可见性不应高于对锁的可见性。在您的示例中,派生实现可以在不获取锁的情况下更改_stringArrayList
(因为它是受保护的),从而破坏不变量。我会使它们同时为private
,并且如果必须的话,只通过(正确锁定)方法向派生类公开_stringArrayList
。