Mar*_*cel 11 .net c# multithreading properties thread-safety
我想对自动实现的属性进行线程安全的读写访问.我在C#/ .NET框架中缺少这个功能,即使在它的最新版本中也是如此.充其量,我会期待类似的东西
[Threadsafe]
public int? MyProperty { get; set; }
Run Code Online (Sandbox Code Playgroud)
我知道有很多代码示例可以实现这一点,但我只是想确保在使用.NET框架方法之前仍然无法实现这一点.我错了吗?
编辑:正如一些答案详细阐述了原子性,我想声明我只想拥有它,据我所知:只要(并且不超过)一个线程正在读取属性的值,没有其他线程允许更改值.因此,多线程不会引入无效值.我选择了int?键入,因为这是我目前关注的问题.
Mar*_*ell 16
正确; 没有这样的设备.据推测,你正试图防止读取该字段,而另一个线程已经改变了一半(原子性)?请注意,许多(小)基元从这种类型的线程问题本质上是安全的:
5.5变量引用的原子性
读取和下列数据类型的写是原子:
bool
,char
,byte
,sbyte
,short
,ushort
,uint
,int
,float
,和引用类型.此外,在先前列表中具有基础类型的枚举类型的读取和写入也是原子的.
但说实话,这只是穿越冰山的一角; 它本身通常仅仅具有线程安全属性是不够的; 最次同步块的范围必须更不是仅仅一个读/写.
根据访问配置文件,还有许多不同的方法可以使线程安全.
lock
?ReaderWriterLockSlim
?Box<T>
,Box<int?>
在这种情况下是a )Interlocked
(在所有的伪装)volatile
(在某些情况下;它不是魔杖...)(更不用说使其成为不可变的(通过代码,或者只是选择不改变它,这通常是使其成为线程安全的最简单方法)
我在这里回答要添加Marc的答案,他说"根据访问配置文件,还有很多不同的方法来制作线程安全的东西".
我只是想补充一点,原因在于,有很多方法都不是线程安全的,当我们说某些东西是线程安全的时候,我们必须明确提供什么样的安全性.
对于几乎任何可变对象,将有一些方法来处理它不是线程安全的(注意几乎任何,异常即将到来).考虑一个具有以下(线程安全)成员的线程安全队列; 入队行动,出列行动和计数财产.通过内部锁定每个成员,甚至使用无锁技术构建其中一个相对容易.
但是,假设我们使用了这样的对象:
if(queue.Count != 0)
return queue.Dequeue();
Run Code Online (Sandbox Code Playgroud)
上面的代码不是线程安全的,因为不能保证在(线程安全)Count
返回1之后,另一个线程不会出列,从而导致第二个操作失败.
它在许多方面仍然是一个线程安全的对象,特别是即使在这种失败的情况下,失败的出队操作也不会使对象进入无效状态.
为了使对象在任何给定的操作组合中成为一个完整的线程安全的,我们必须使它在逻辑上不可变(可以通过线程安全操作来内部可变性更新内部状态作为优化 - 例如通过memoisation或根据需要从一个数据源加载,但在室外,必须出现不可变的),或者严重降低外部操作的可能数量(我们可以创建一个线程安全的队列只有Enqueue
和TryDequeue
这始终是线程安全的,但是这既降低了操作可能,并且还强制将失败的dequeue重新定义为不失败,并强制改变我们之前版本调用代码的逻辑.
其他任何东西都是部分保证.我们免费得到一些部分保证(正如Marc所说,对于某些自动属性而言,就单独原子而言已经是线程安全的 - 在某些情况下,这是我们需要的所有线程安全性,但在其他情况下不会去任何地方附近很远).
让我们考虑一个属性,将这个部分保证添加到我们还没有得到它的情况.它对我们有多大的价值?嗯,在某些情况下它会是完美的,但在其他情况下它不会.在出列之前回到我们的测试案例,有这样的保证Count
并没有多大用处 - 我们有这种保证,并且代码在多线程条件下仍然以一种在单线程条件下不会出现的方式失败.
更重要的是,将此保证添加到尚未拥有它的情况下至少需要一定程度的开销.一直担心开销可能是过早的优化,但增加开销以获得没有收益是过早的悲观,所以不要这样做!更重要的是,如果我们提供更广泛的并发控制以使一组操作真正是线程安全的,那么我们将使更窄的并发控制变得无关紧要,并且它们变得纯粹的开销 - 所以我们甚至没有从我们的在某些情况下开销; 它几乎总是纯粹浪费.
还不清楚并发问题的范围有多宽或多窄.我们是否只需要锁定(或类似)该属性,还是需要锁定所有属性?我们是否还需要锁定非自动操作,甚至是可能的?
这里没有一个好的单一答案(在编写自己的解决方案时,它们可能是难以回答的问题,不要介意在其他人使用此[Threadsafe]属性时会产生此类代码的代码中回答它).
此外,任何给定的方法都会有一组不同的条件,其中可能发生死锁,活锁和类似问题,因此我们实际上可以通过将线程安全性视为我们可以盲目地应用于属性的东西来减少线程安全性.
没有能够找到这些问题的单一通用答案,没有提供单一通用实现的好方法,任何这样的[Threadsafe]属性最多只是非常有限的价值.最后,在使用它的程序员的心理层面上,很可能导致他们创建了一个线程安全的类,而事实上他们没有; 这会使它实际上比无用更糟糕.
归档时间: |
|
查看次数: |
2428 次 |
最近记录: |