List.Add是一个实例成员。这意味着它不能保证是线程安全的。这是什么意思?
可能性1.如果两个线程在不同的实例上调用.Add,根据月亮的相位可能会出现意外结果?
可能性2.如果两个线程在同一个实例上调用.Add,则根据月亮的相位可能会出现意外结果,如果实例不同则没有潜在问题。
可能性3.微软根本不希望人们使用线程,因此他们认为.NET不明确。
答案 0 :(得分:10)
可能性1并非如此。对于一个实例方法来说,为其他实例引起问题是非常不寻常的,这可以清楚地记录下来(不仅仅是声明指出这一点,而且还有一些理由,因为这通常是编码非常糟糕的迹象,所以如果有一个很好的理由,它会被指出)。
可能性3并非如此,因为他们刚刚记录了线程行为。
可能性2部分就是这种情况。但是,交互也可以是一个调用Add的线程,另一个调用另一个非线程安全的实例方法。
框架提供的大多数可变类对于静态成员是线程安全的,而对于实例方法则是非线程安全的。这是有充分理由的。
如果静态方法不是线程安全的,那么以线程安全的方式调用该方法是非常困难的,特别是如果该类可能由不同人编写的不同代码层使用。这使得使这些方法线程安全的努力几乎总是合理的。大多数这样的成员也相对容易制作线程安全(如果避免使用可变静态,这总是一件好事可以避免)。
单个对象的大量使用将由一个线程一次完成,而不会被另一个线程访问。这使得难以确保正确性,如果出现问题则存在死锁风险,以及强加于性能的开销,难以证明其合理性。对于使用该类的人来说,确保多线程使用的实例以线程安全的方式使用时,相对也很容易。
在那里“非常重视”,因为编写线程安全代码并不总是那么容易。有时它很容易(不可变的类需要一些工作才能使非线程安全!),但更常见的是它很难(因此在这里和其他地方有很多关于这个主题的问题)。
然而,这正是在这种情况下应该给用户带来负担的原因。要使这样一个类完全线程安全是如此困难(事实上,有时可证明是不可能的),结果对于大多数用户来说是不可接受的,他们是最能判断在给定情况下需要什么保护的人。
答案 1 :(得分:4)
我认为,List.Add
是个坏榜样。 List.Remove
更好,因为实际上存在值得注意的线程问题。一个线程可以尝试访问一个项目,而另一个线程会尝试在其上调用List.Remove
。现在,当尝试访问该项时,可能会删除该项,从而导致NullReferenceException
。但总的来说,这主要是免责声明,因为没有锁定机制。只要两个线程可以尝试访问同一个对象或同一段代码来防止此类问题,请记住lock
。
答案 2 :(得分:0)
因为在实例成员上没有实现锁定机制所以他们将该免责声明放在MSDN网站上。
答案 3 :(得分:0)
如果两个线程同时尝试对List的同一个实例执行不同的操作,则可能会发生错误。多线程同时对列表执行不同操作的唯一情况是所有线程都在读取时。我认为使用列表是安全的,但即使对于所有容器类来说也许不一定安全。
.net中的列表等集合通常存储在数组中;如果集合对于其数组而言太大,则将分配更大的数组。如果多个线程处理相同的集合并且没有任何互锁,则一个线程可能会添加会增加列表的内容,然后其他线程尝试在列表的时间之间更改列表复制以及使用新列表开始收集的时间。这可能会导致更改丢失,或导致其他不良事件发生。
答案 4 :(得分:0)
如果两个不同的线程在没有同步/锁定的情况下修改相同的列表,则可能会导致问题。使用不同列表的两个线程将没问题。对于大多数类来说也是如此 - 实际上非常非常少的类明确声明“此类是线程安全的”。但是如果您不在线程之间共享(访问)实例,则几乎所有类都是安全的。如果一个类即使在线程没有共享实例时也会中断,那么文档就会这么说 - 但是这种情况非常糟糕,我希望MS能够将它排除在API之外。
微软以一种巨大的理由说出并按照他们的方式做事(线程安全明智):
线程安全很难。
没有办法同步适合每个人的事情。并且当您不必要时锁定和同步可能会导致性能甚至稳定性。全方位的最佳解决方案是让代码完成它应该做的事情,没有同步,并让需要线程安全的人自己安排。