我有一个名为DataStorage的单例obj,用于在我的应用程序中存储常用数据;其中一个是名为myTable的数据表,它将被多个线程读取和写入。我有一个私有对象用作DataStorage中的锁,即
private object lockObj = new object();
我已经围绕访问myTable包装了锁:
private DataTable myTable;
public DataTable MyTable
{
get
{ lock(lockObj) { return myTable; } }
set
{ lock(lockObj) { myTable = value; } }
}
另一个对象,即MyObj将获取此数据表,对其执行选择,然后从Select中检索检索到的DataRow []中的某个值。我已经读过Select不是线程安全的,所以我写了这样的代码:
// lock on MyTable
DataTable dt = DataStorage.Instance.MyTable;
lock (MyObjLockObj) // lock object for MyObj class
{
// do a select, then modify value in the returned row
DataRow[] foundRows = dt.Select("some expression");
foundRows[0]["some col"] = 123456;
}
问题: 1.总的来说,这段代码是否是线程安全的?
当我在MyObj中修改检索到的DataRow时,MyTable setter是否确保其线程安全?因为setter用于设置DataTable,而不是DataRow。
我是否应该将访问DataStorage.Instance.MyTable的代码移动到与Select相同的锁定块中?
提前致谢。
答案 0 :(得分:1)
你的代码不是线程安全的,因为:
对属性的锁定只能确保一个线程不能同时使用get / set属性,但是在它们有权访问后,它们可以同时更改它。
即
线程可以使用MyTable
属性来获取数据表的instacne。
之后,另一个thred可以使用该属性来获取相同的实例。
然后在他们都可以访问myTable
之后,他们都可以直接写入它而没有同步。
所以将访问者移动到一个锁中也不会有帮助。
最好提供更改数据的方法,并在这些方法中进行同步,而不是直接公开数据表。
答案 1 :(得分:1)
getter和setter中的那些锁完全没用。 'myTable = value'操作已经是原子操作,无需将其包装在锁中。当然,这个锁与修改行无关。然而,该锁(MyObjLockObj)确保只有一个线程将访问您的数据表,如果您的应用程序使用相同的MyObjLockObj。如果您不经常使用该数据表,那么这不会有问题,但是如果您的应用程序只是搞乱该数据表,那么您将遇到性能问题。
总而言之,在多线程应用程序中使用单个数据表是一个糟糕的决定。如果从DB读取该数据表的数据,则应该为每个线程读取一个数据表,修改其行并将更改提交回DB。