【问题标题】:Good advices to use EF in a multithread program?在多线程程序中使用 EF 的好建议?
【发布时间】:2010-07-15 17:49:03
【问题描述】:

对于在多线程程序中使用 EF,您有什么好的建议吗?

我有 2 层:

  • 一个 EF 层来读取/写入我的数据库
  • 一个多线程服务,它使用我的实体(读/写)并进行一些计算(我在框架中使用任务并行库)

如何在每个线程中同步我的对象上下文? 你知道让它发挥作用的好模式吗?

【问题讨论】:

  • 您的问题到底是什么? ObjectContext 是一个工作单元。它不是线程安全的,也不需要。每个任务使用一个 OC,你会没事的。在线程之间共享一个,你会遇到麻烦。
  • 如果我每个任务使用一个 OC,我想同步我的 OC。例如,当我在 thread1 中执行 SaveChanges() 时,我想在 thread2 中刷新我的实体。
  • 如果你想这样做,你最好不要打扰线程,因为你的任务实际上并不是独立的。
  • 您可能想阅读 - 虽然有点老,但仍然有效 - 这篇文章 blog.cincura.net/230902-multithreading-with-entity-framework

标签: c# .net entity-framework multithreading locking


【解决方案1】:

好的建议是 - 只是不要 :-) EF 几乎无法在一个线程中存活下来 - 野兽的本性。

如果您绝对必须使用它,请制作最轻量的 DTO-s,在获得数据后立即关闭 OC,重新打包数据,生成线程只是为了进行计算而不是其他任何事情,等到它们完成,然后创建另一个 OC 并将数据转储回数据库,对其进行协调等。

如果另一个“主”线程(通过 TPL 产生 N 个计算线程的那个)需要知道某个线程何时完成触发事件,只需在另一个线程中设置一个标志,然后让它的代码检查它的标志通过创建新的 OC 并在必要时协调数据来循环并做出反应。

如果您的情况更简单,您可以调整它 - 关键是您只能设置一个标志并让另一个线程在准备好时做出反应。这意味着它处于稳定状态,已经完成了它正在做的任何事情,并且可以在不冒竞争条件的情况下做事。使用交换操作重置标志(一个 int)并保留一些计时数据,以确保您的线程不会在一段时间内再次做出反应 T - 否则他们可以一生只查询 DB。

【讨论】:

    【解决方案2】:

    这就是我在我的场景中实现它的方式。

    var processing= new ConcurrentQueue<int>();
    
    
    //possible multi threaded enumeration only processed non-queued records
    Parallel.ForEach(dataEnumeration, dataItem=>
    {
         if(!processing.Contains(dataItem.Id))
         {
             processing.Enqueue(dataItem.Id);
    
              var myEntityResource = new EntityResource();
    
              myEntityResource.EntityRecords.Add(new EntityRecord
                                          {
                                            Field1="Value1", 
                                            Field2="Value2"
                                          }
                                   );
    
               SaveContext(myEntityResource);
    
           var itemIdProcessed = 0;
           processing.TryDequeue(out itemIdProcessed );
    
         }
    
    }
    
    public void RefreshContext(DbContext context)
        {
            var modifiedEntries = context.ChangeTracker.Entries()
                .Where(e => e.State == EntityState.Modified || e.State == EntityState.Deleted);
            foreach (var modifiedEntry in modifiedEntries)
            {
                modifiedEntry.Reload();
            }
        }
    
    public bool SaveContext(DbContext context,out Exception error, bool reloadContextFirst = true)
        {
            error = null;
            var saved = false;
            try
            {
                if (reloadContextFirst)
                    this.RefreshContext(context);
                context.SaveChanges();
                saved = true;
            }
            catch (OptimisticConcurrencyException)
            {
                //retry saving on concurrency error
                if (reloadContextFirst)
                    this.RefreshContext(context);
                context.SaveChanges();
                saved = true;
            }
            catch (DbEntityValidationException dbValEx)
            {
                var outputLines = new StringBuilder();
                foreach (var eve in dbValEx.EntityValidationErrors)
                {
                    outputLines.AppendFormat("{0}: Entity of type \"{1}\" in state \"{2}\" has the following validation errors:",
                        DateTime.Now, eve.Entry.Entity.GetType().Name, eve.Entry.State);
                    foreach (var ve in eve.ValidationErrors)
                    {
                        outputLines.AppendFormat("- Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage);
                    }
                }
    
                throw new DbEntityValidationException(string.Format("Validation errors\r\n{0}", outputLines.ToString()), dbValEx);
            }
            catch (Exception ex)
            {
                error = new Exception("Error saving changes to the database.", ex);
            }
            return saved;
        }
    

    【讨论】:

      【解决方案3】:

      我认为 Craig 对您的应用程序不需要线程可能是正确的。但您可能会在模型中寻找 ConcurrencyCheck 的用途,以确保您不会“覆盖”您的更改

      【讨论】:

        【解决方案4】:

        我不知道您的应用程序实际上有多少是在进行数字运算。如果速度是使用多线程的动机,那么退后一步收集有关瓶子下一个位置的数据可能会有所收获。

        在很多情况下,我发现应用程序使用数据库服务器的限制因素是用于存储的 I/O 系统的速度。例如,硬盘驱动器磁盘的速度及其配置可能会产生巨大影响。一个 7,200 RPM 的硬盘驱动器每秒可以处理大约 60 次事务(大致数字取决于许多因素)。

        所以我的建议是先测量然后找出下一个瓶子在哪里。您甚至可能不需要线程。这将使代码更容易维护,而且质量很可能会更高。

        【讨论】:

          【解决方案5】:

          “如何在每个线程中同步我的对象上下文?” 这将是艰难的。首先,SP 或 DB 查询可以有并行执行计划。因此,如果您在对象上下文上也有并行性,则必须手动确保您有足够的隔离,但又要足够长,以免您持有锁太久而导致死锁。

          所以我会说不需要这样做。

          但这可能不是您想要的答案。所以你能解释一下你想用这个多线程实现什么吗?它是更多的计算限制还是 IO 限制。如果它是 IO 绑定的长期运行操作,请查看 Jeff Richter 的 APM。

          【讨论】:

            【解决方案6】:

            我认为您的问题更多是关于线程之间的同步,而 EF 在这里无关紧要。如果我理解正确,您想在主线程执行某些操作时通知一组线程 - 在本例中为“SaveChanges()”操作。这里的线程就像客户端-服务器应用程序,其中一个线程是服务器,其他线程是客户端,您希望客户端线程对服务器活动做出反应。

            正如有人注意到的那样,您可能不需要线程,但让我们保持原样。

            只要您要为每个线程使用单独的 OC,就不必担心死锁。

            我还假设您的客户端线程是某种循环中的长时间运行线程。如果您希望代码在客户端线程上执行,则不能使用 C# 事件。

            class ClientThread {
            public bool SomethingHasChanged;
            
              public MainLoop()
              {
                Loop {
                  if (SomethingHasChanged)
                  { 
                    refresh();
                    SomethingHasChanged = false;
                  }
            
                  // your business logic here
            
            
                } // End Loop
              }
            }
            

            现在的问题是如何在所有客户端线程中设置标志?您可以在主线程中保留对客户端线程的引用并循环它们并将所有标志设置为 true。

            【讨论】:

              【解决方案7】:

              当我使用 EF 时,我只有一个 ObjectContext,我将所有访问同步到它。

              这并不理想。您的数据库层实际上是单线程的。但是,它确实在多线程环境中保持线程安全。就我而言,繁重的计算根本不在数据库代码中——这是一个游戏服务器,所以游戏逻辑当然是主要的资源消耗。所以,我对多线程数据库层没有任何特别的需求。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-03-13
                • 2011-03-14
                • 2017-02-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-09-30
                • 1970-01-01
                相关资源
                最近更新 更多