【发布时间】:2023-02-19 02:05:17
【问题描述】:
前言
我知道 List 不是线程安全的,并且知道并发集合的存在,例如 ConcurrentBag、ConcurrentQueue 等,并且在某种程度上知道如何使用锁。我只想知道如果我这样做会有什么危险。
问题
比如说,如果我同时运行 2 个线程,一个向 List 添加值,另一个从同一个 List 中删除元素,会有什么危险?
我知道,如果我在两个线程中添加元素,危险可能很严重,因为 List 在内部调整集合的大小和重新索引,并且“种族”将破坏它,但 Add 和 Remove 本质上是相反的操作,我没有看到其中有任何“冲突”。
我也知道 Remove 也在内部调整集合的大小和重新索引,但老实说我不知道它们内部是如何工作的,我真的不知道“2 个相反的操作是否仍然会破坏数据”。如果是,以什么方式? (除了“可能没有什么要删除的明显危险,因为元素还没有被添加”)。
【问题讨论】:
-
它们在内部如何工作无关紧要。事实上,实现可以根据平台、环境甚至 .Net 版本而改变。关键是它不是线程安全的。这对你来说应该足够了。至于实现细节:在列表上添加和删除通常涉及内部指针递增和递减。这些操作通常不是原子的。此外,如果不是易变的(这是常见情况),则在线程外不可见。这可能会导致奇怪的问题,比如一个线程认为它需要调整大小,因为他从未看到另一个线程删除任何东西。
-
并且无论什么特殊用例现在可能是线程安全的,都不能保证将来是。
-
您可以同时从两个线程对一个对象执行的唯一操作是原子操作(如设置或读取整数)。当您向集合中添加内容或从中删除内容时,您正在访问集合的内部状态(例如,将对象添加到内部数组或将其插入链表,并更新计数)。即使是简单的“读-修改-写”操作(比如读取一个整数并递增它)也不能在没有同步或互锁的情况下以线程安全的方式完成。
标签: c# multithreading thread-safety