这个赋值是线程安全的吗?



Load()中的_dict赋值是线程安全的吗?这意味着在Load()_dict变化期间,Get方法将不会从字典中读取。我知道指针赋值是原子的,所以我加了锁,这样编译器就不会对代码重新排序了。

public class A
{
private readonly object _lock = new object();
private Dictionary<int, int> _dict = new Dictionary<int, int>();
public Task Load()
{
Dictionary<int, int> temp;
lock (_lock)
{
var list = GetFromDb(); //Some db query that returns an updated data
temp = list.toDictionary(<some conversion>);
}
_dict = temp;
}
public int Get(int key)
{
return _dict[key];
}
}

不,这段代码不是线程安全的,因为当Load方法中的字典发生变化时,不能通过Get()方法进行读取。您还需要在Get方法中使用锁来实现这一点:

public class A
{
private readonly object _lock = new object();
private Dictionary<int, int> _dict = new Dictionary<int, int>();
public async Task Load()
{
lock (_lock)
{
var list = await GetFromDb(); //Some db query that returns an updated data
_dict = list.toDictionary(<some conversion>);
}
}
public int Get(int key)
{
lock(_lock)
{
return _dict[key];
}
}
}

在这段代码中,如果线程1调用Load,线程2调用Get,而线程1仍然执行Load,线程2将等待,直到线程1执行完Load

不,如果一个字段在多个线程中被改变,它应该被标记为volatile。否则,可能会出现Get-方法内联在循环中的情况,并且引用不会更新,因为编译器在没有同步的情况下将程序视为单线程进行优化。出现这种情况的可能性可能很小,但是很容易将该字段标记为易失性。

对于加载字典,对其进行一些处理,然后将其分配给共享字段,这应该是安全的。赋值是原子的,查找引用也是原子的。因此,Get-方法要么获取旧字典,要么获取新字典。但是由于_dict-字段所引用的字典永远不会改变,因此该示例应该是线程安全的。