110,566
社区成员
发帖
与我相关
我的任务
分享
class MyList
{
int count;
int[] items = new int[1024];
public void Add(int val)
{
// 方法一
count++;
items[count - 1] = val;
}
public int GetLast()
{
return items[count - 1];
}
}
该实现在添加(写入)的时候,一先增加总数,二再写入数组。这种情况,它就对读操作就不是线程安全的。
因为另外一个线程GetLast进行读操作的时候,可能正好发生在一和二操作之间,它将返回的,将是一个未定义的元素。
一个简单的更改就可以避免这个问题(限于一个写入线程):
// 方法二
items[count] = val;
count++;
先把内容写入,再提交总数变化。如此以来,提交后,读线程一定能得到正确的数据;提交前,读线程也看不到变化,同样能得到正确的数据。
这个例子要解释的就是,要作到单一写入线程安全,并不一定要进行同步操作。因此,不需要有‘导致多个线程读的效率降低呢’的担忧。如果你有心确认这点,可以用Reflector等查看Hashtable的代码,看看有何种的同步机制。
有趣的是,Hashtable.Synchronized(hashtable1),可以返回一个多重写线程安全Hashtable。实际上,它返回了一个继承于Hashtable的SyncHashtable,在添加的时候,对SyncRoot加锁:
public override void Add(object key, object value)
{
lock (this._table.SyncRoot)
{
this._table.Add(key, value);
}
}
要小心的是,Hashtable.Synchronized(hashtable1)保证每个独立操作是安全的,但不保证多个操作的线程安全,比如:
Hashtable syncedTable = Hashtable.Synchronized(new Hashtable());
void Thread1()
{
syncedTable.Add("A", 123);
int i = (int)syncedTable["A"]; //可能会出现异常,如果“A"被Thread2删除了
}
void Thread2()
{
syncedTable.Remove("A");
}
void output(){
object token= new object();
lock (token)
{
写入操作
}}