C# 使链接列表线程安全
我知道以前有人问过这个问题(我会继续研究),但我需要知道如何以线程安全的方式创建特定的链表函数。我当前的问题是,我有一个线程循环遍历链表中的所有元素,而另一个线程可能会将更多元素添加到此列表的末尾。有时,一个线程试图向列表中添加另一个元素,而第一个线程正忙于迭代该元素(这会导致异常) 我在考虑只添加一个变量(布尔标志)来表示列表当前正忙于迭代,但接下来如何检查它并与第二个线程一起等待(如果它等待,则可以,因为第一个线程运行得非常快)。我能想到的唯一方法是通过使用while循环不断地检查这个busy标志。我意识到这是一个非常愚蠢的想法,因为它会导致CPU在不做任何有用的事情的情况下努力工作。现在,我在这里要求更好的洞察力。我读过锁之类的东西,但这似乎与我的情况无关,但也许我错了 与此同时,我会继续在互联网上搜索,如果找到了解决方案,我会发回邮件 编辑: 让我知道我是否应该发布一些代码来澄清问题,但我会尝试更清楚地解释它 所以我有一个类,其中有一个链表,包含需要处理的元素。我有一个线程通过函数调用(让我们称之为“processElements”)遍历这个列表。我有第二个线程,它以非确定性的方式向进程添加元素。但是,有时它会在processElements运行时尝试调用此addElement函数。这意味着,当第一个线程遍历某个元素时,该元素将被添加到链表中。这是不可能的,会导致异常。希望这能把事情弄清楚 我需要添加新元素的线程,直到processElements方法执行完毕C# 使链接列表线程安全,c#,multithreading,C#,Multithreading,我知道以前有人问过这个问题(我会继续研究),但我需要知道如何以线程安全的方式创建特定的链表函数。我当前的问题是,我有一个线程循环遍历链表中的所有元素,而另一个线程可能会将更多元素添加到此列表的末尾。有时,一个线程试图向列表中添加另一个元素,而第一个线程正忙于迭代该元素(这会导致异常) 我在考虑只添加一个变量(布尔标志)来表示列表当前正忙于迭代,但接下来如何检查它并与第二个线程一起等待(如果它等待,则可以,因为第一个线程运行得非常快)。我能想到的唯一方法是通过使用while循环不断地检查这个bus
- 任何在这个问题上绊倒的人。被接受的答案将给你一个快速、简单的解决方案,但请查看下面Brian Gideon的答案,以获得更全面的答案,这肯定会给你更多的洞察力李>
- 下面是一个如何使用锁同步访问列表的快速示例:
private readonly IList<string> elements = new List<string>(); public void ProcessElements() { lock (this.elements) { foreach (string element in this.elements) ProcessElement(element); } } public void AddElement(string newElement) { lock (this.elements) { this.elements.Add(element); } }
异常可能是通过“代码> IEnumerator < /COD>”在集合迭代期间更改集合的结果。您可以使用很少的技术来维护线程安全。我将按困难的顺序介绍它们
锁定所有内容 到目前为止,这是访问数据结构线程安全的最简单、最简单的方法。当读操作和写操作的数量相等时,此模式工作良好
更新: 因此,我在评论部分提出了两个问题:LinkedList<object> collection = new LinkedList<object>(); void Write() { lock (collection) { collection.AddLast(GetSomeObject()); } } void Read() { lock (collection) { foreach (object item in collection) { DoSomething(item); } } }
- 为什么写端的
而不是lock(lockobj)
lock(collection)
- 为什么在读取端
local=collection
关于第一个问题,考虑C编译器如何扩展<代码>锁< /代码> .<
这里的问题是,void Write() { bool acquired = false; object temp = lockobj; try { Monitor.Enter(temp, ref acquired); var copy = new LinkedList<object>(collection); copy.AddLast(GetSomeObject()); collection = copy; } finally { if (acquired) Monitor.Exit(temp); } }
随时都可能被调出。这将是一个难以找到的bug。这就是为什么在执行此模式时,我总是在读取端提取本地引用的原因。确保在每个读取操作上使用相同的集合集合
再说一遍,因为这样的问题非常微妙,除非你真的需要,否则我不建议你使用这种模式。你错了。锁正是你所需要的。这很好(因此“也许我错了”)。不过谢谢你,我会一直坚持下去,直到我弄明白如何应用它+1为您提供解决方案,您需要使列表线程安全,而不是“a”方法。这已经被问了很多次了。你不能只让一个方法线程安全。线程安全在不同的上下文中可能意味着不同的事情。确切地告诉我们你想完成什么。好的,我会详细介绍一下!哦,哇,看起来很简单。我看到的所有示例都有Object o=new Object(),然后他们会锁定它,这让我很困惑。但我想他们给出了一个普遍的例子。太多了,伙计@Denzil您需要一些私有(私有是重要的)字段,这些字段是任何其他代码库都无法访问的。(如果在其他地方可以访问,而您没有做好准备,那么很可能会出现死锁。)通常,没有适合此目的的对象,因此人们创建一个新对象(它没有字段,因此占用的内存量最小)只是为了锁定。大多数时候这是最好的;锁定任何其他内容通常都是一个等待发生的错误。@Denzil:根据经验,您可以使用任何声明为private和readonly的字段。如果您碰巧已经在使用一个(例如第一个示例中的
集合),那么您就可以开始了。如果没有,就专门为这个目的创建一个新的对象。哦,我明白了,太棒了。你应该再来一次。但如果我可以问,如果解释不是太长,为什么对象必须是只读的?是不是这样,当某个进程被锁定时,我们不会从外部对其进行更改?。。。哦,我知道Servy已经回答了这个问题,这是我们的预防措施,这样我们就不会在内部改变它。(private修饰符无论如何都会阻止外部访问。)如果允许更改字段的值,那么您将面临两个竞速线程可能在不同实例上获得锁的风险,并且两个线程都被允许同时进行(访问不同的集合)。测验…有人能猜出为什么1)我使用了元素
而不是写端的lock(lockobj)
,以及2)在“复制、修改、交换”模式中通过读端的lock(collection)
使用本地引用?我将详细研究并local=collection
LinkedList<object> collection = new LinkedList<object>(); void Write() { lock (collection) { collection.AddLast(GetSomeObject()); } } void Read() { LinkedList<object> copy; lock (collection) { copy = new LinkedList<object>(collection); } foreach (object item in copy) { DoSomething(item); } }
object lockobj = new object(); volatile LinkedList<object> collection = new LinkedList<object>(); void Write() { lock (lockobj) { var copy = new LinkedList<object>(collection); copy.AddLast(GetSomeObject()); collection = copy; } } void Read() { LinkedList<object> local = collection; foreach (object item in local) { DoSomething(item); } }
void Write() { bool acquired = false; object temp = lockobj; try { Monitor.Enter(temp, ref acquired); var copy = new LinkedList<object>(collection); copy.AddLast(GetSomeObject()); collection = copy; } finally { if (acquired) Monitor.Exit(temp); } }
void Read() { object x = collection.Last; // The collection may get swapped out right here. object y = collection.Last; if (x != y) { Console.WriteLine("It could happen!"); } }
- 为什么写端的