简体   繁体   English

在单独的线程上处理保存

[英]Handling a save on a separate thread

I've got a class that contains (among many other things) a list of items that needs to be saved to disk as often as realistically possible. 我有一个包含(除其他事项外)一系列项目的类,这些项目需要尽可能实际地保存到磁盘中。 In order to allow this, whenever any change is made, it uses a timer to delay saving to disk by half a second, and if any further changes happen within that half second, it delays the timer; 为了做到这一点,无论何时进行任何更改,它都会使用计时器将保存到磁盘的时间延迟半秒,如果在此半秒内发生任何进一步的更改,则会延迟计时器。 in other words, it'll save to disk whenever there's been no changes within half a second. 换句话说,只要半秒内没有任何更改,它将保存到磁盘。

However, I'm hitting threading issues where the list could be modified in the middle of the save. 但是,我遇到了线程问题,可以在保存过程中修改列表。 I've come up with a way of fixing it, but it feels like a bit of a hack, and I'm fairly sure there has to be a better way. 我已经提出了一种修复它的方法,但是感觉有点像破解,而且我很确定必须有一个更好的方法。

The save method locks the thread on a private instance of 'object' during a save operation, but this doesn't stop the list being updated, as it's a separate class, and doesn't have access to that private lock. save方法在保存操作期间将线程锁定在“对象”的私有实例上,但这不会阻止列表的更新,因为它是一个单独的类,并且无法访问该私有锁。

So, to resolve this what I've done is added an 'about to change' event to the list class (not a bad feature for it to have anyway), and the containing class listens to this event, and locks on the same private lock as the save method, but takes no action. 因此,要解决此问题,我要在列表类中添加一个“即将更改”事件(无论如何,它不是一个不好的功能),并且包含类侦听此事件,并锁定相同的私有对象锁定为保存方法,但不执行任何操作。 This forces the thread to wait until the save is complete before allowing the list to be updated, even though the list itself has no knowledge of it's containing object, or what it's doing. 即使列表本身不知道它包含的对象或它在做什么,这也会迫使线程等待直到保存完成才允许更新列表。

Here's the (seriously simplified) source: 这是(严重简化的)源:

public class ItemContainer
{
  private ItemList _itemList = new ItemList();
  private object _padlock = new object();

  public ItemContainer()
  {
    _itemList.ItemAdded += StartTimedSave;
    _itemList.ItemAdding += (sender, e) => { lock(_padlock) { } };
  }

  private StartTimedSave()
  {
    // starts or resets a half second timer to call Save();
  }

  private void Save()
  {
    lock(_padlock)
    {
      foreach (object obj in _itemList)
      {
        // save it.
      }
    }
  }
}

public class ItemList
{
  public event EventHandler<CancelEventArgs> ItemAdding;
  public event EventHandler ItemAdded;

  public List<object> _list;

  public void Add(object obj)
  {
    if (ItemAdding.RaiseWithCancel(this)) // extension method
      return;
    _list.Add(obj);
    ItemAdded.Raise(this); // another extension method
  }
}

I can't realistically have the list hold a reference to it's containing object, as the list is a fairly generalized class that may or may not be contained by such an object. 我实际上不能让列表包含对其包含对象的引用,因为列表是一个相当笼统的类,此类对象可能包含也可能不包含。

Is an event handler with an empty lock { } block really the way to go with this? 带有空lock { }块的事件处理程序真的是解决这个问题的方法吗? I'm uncomfortable with the notion of allowing an event handler to interfere with the operation of the object that raised it, but I can't see a better way. 我对允许事件处理程序干扰引发它的对象的操作的想法感到不舒服,但是我找不到更好的方法。

When saving, make a copy of the list in memory and save that -- this will reduce the exposure of the data to updates in other threads. 保存时,将列表复制到内存中并保存-这将减少数据暴露给其他线程中的更新。

To be thread-safe, ask the ItemList class to create and return the list copy and the ItemList class can lock the internal list long enough to make the copy: 为了确保线程安全,请让ItemList类创建并返回列表副本,并且ItemList类可以将内部列表锁定足够长的时间以进行复制:

public List<object> CreateListCopy()
{
    List<object> result = new List<object>();
    lock(_list)
    {
        foreach(object o in _list)
        {
            result.Add(o.Clone());
        }
    }

    return result;
}

Then you save the result of CreateListCopy() in your save thread. 然后,将CreateListCopy()的结果保存到保存线程中。

EDIT: When copying the list of items, don't forget to ensure you are using deep copy semantics as you don't want to just be copying the references; 编辑:复制项目列表时,不要忘记确保您使用的是深层复制语义,因为您不想仅复制引用; you want full, completely separate copies of the data. 您需要完整,完全独立的数据副本。

this does nothing: 这什么都不做:

lock(_padlock) { } 锁(_padlock){}

Lock holds while block is executing - no point to lock empty block. 块执行期间锁定保持-没有指向锁定空块的指示。

If all problem is with that private lock object, just make public getter for it. 如果所有问题都与该私有锁对象有关,则只需为其公开获取即可。 Make sure that anything that modifies or reads collection locks it. 确保任何修改或读取集合的东西都将其锁定。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM