Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使链接列表线程安全_C#_Multithreading - Fatal编程技术网

C# 使链接列表线程安全

C# 使链接列表线程安全,c#,multithreading,C#,Multithreading,我知道以前有人问过这个问题(我会继续研究),但我需要知道如何以线程安全的方式创建特定的链表函数。我当前的问题是,我有一个线程循环遍历链表中的所有元素,而另一个线程可能会将更多元素添加到此列表的末尾。有时,一个线程试图向列表中添加另一个元素,而第一个线程正忙于迭代该元素(这会导致异常) 我在考虑只添加一个变量(布尔标志)来表示列表当前正忙于迭代,但接下来如何检查它并与第二个线程一起等待(如果它等待,则可以,因为第一个线程运行得非常快)。我能想到的唯一方法是通过使用while循环不断地检查这个bus

我知道以前有人问过这个问题(我会继续研究),但我需要知道如何以线程安全的方式创建特定的链表函数。我当前的问题是,我有一个线程循环遍历链表中的所有元素,而另一个线程可能会将更多元素添加到此列表的末尾。有时,一个线程试图向列表中添加另一个元素,而第一个线程正忙于迭代该元素(这会导致异常)

我在考虑只添加一个变量(布尔标志)来表示列表当前正忙于迭代,但接下来如何检查它并与第二个线程一起等待(如果它等待,则可以,因为第一个线程运行得非常快)。我能想到的唯一方法是通过使用while循环不断地检查这个busy标志。我意识到这是一个非常愚蠢的想法,因为它会导致CPU在不做任何有用的事情的情况下努力工作。现在,我在这里要求更好的洞察力。我读过锁之类的东西,但这似乎与我的情况无关,但也许我错了

与此同时,我会继续在互联网上搜索,如果找到了解决方案,我会发回邮件

编辑: 让我知道我是否应该发布一些代码来澄清问题,但我会尝试更清楚地解释它

所以我有一个类,其中有一个链表,包含需要处理的元素。我有一个线程通过函数调用(让我们称之为“processElements”)遍历这个列表。我有第二个线程,它以非确定性的方式向进程添加元素。但是,有时它会在processElements运行时尝试调用此addElement函数。这意味着,当第一个线程遍历某个元素时,该元素将被添加到链表中。这是不可能的,会导致异常。希望这能把事情弄清楚

我需要添加新元素的线程,直到processElements方法执行完毕

  • 任何在这个问题上绊倒的人。被接受的答案将给你一个快速、简单的解决方案,但请查看下面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)
      而不是写端的
      lock(collection)
      ,以及2)在“复制、修改、交换”模式中通过读端的
      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!");
        }
      }