最佳实践:锁定数据对象

4
我编写了一个处理大型文本文件的应用程序。内部,文本文件被存储为DataObject,其中包含多个数据,如文件的行数、文件路径等。我可以使用应用程序修改这些文件(数据对象)。由于某些方法非常耗时,因此我在任务中运行它们以避免阻塞UI。现在,在非阻塞UI下,我想确保用户不会尝试修改正在任务中处理的文件,因此我想锁定DataObject。为此,我认为可以向DataObject类添加锁定对象。然后,我将以以下方式锁定DataObject:
public class DataObject {
    public object LockObject = new object();
    // ...

    public DataObject() { }
}

public void timeConsumingMethod(DataObject data) {
    Task.Factory.StartNew(new Action(() => {
        lock(data.LockObject) {
            // do work
        }
    }));
}

这种方法可行吗?还有更好的方法吗?


我喜欢提出的解决方案,但如果您尝试从对象中读取数据时要小心 - 这可能会导致同时写入/读取的问题(或者如果您在读取时忘记锁定)。我会添加自己的问题,询问是否应该在获取/设置值时在属性内部进行锁定? - Tatranskymedved
考虑使用 Task.Run 而不是 StartNew,因为后者存在风险 - VMAtm
@VMAtm:我已经阅读了这篇文章。总的来说是一篇不错的博客。我之前还没有想到过这个问题。谢谢。 - MrToast
1个回答

4

永远不要暴露LockObject,隐藏实现细节:

private object m_LockObject = new object();

下一个问题是:为什么timeConsumingMethod接收DataObject而不是在DataObject内实现:
public class DataObject {
  // locking object is a private implementation detail
  private object m_LockObject = new object();

  // TheMethod works with "this" DataObject instance, that's why
  // the method belongs to DataObject
  // let's return Task (e.g. to await it)
  // Think on method's name; 
  public Task TheMethodAsync() {
    // Task.Factory.StartNew is evil
    return Task.Run(() => {
      lock (m_LockObject) {
        // ...
      } 
    });
  }

  ...
}

那么只需调用该方法即可。
 public void timeConsumingMethod(DataObject data) {
   // When designing public methods do not forget about validation
   if (null == data)
     throw new ArgumentNullException("data");

   // Think on awaiting the Task returned:
   // "i run them in a Taks ... to avoid blocking the UI"
   // await data.TheMethodAsync();  
   data.TheMethodAsync();  

   ...
 }

我不想在DataObject中实现该方法,因为我有许多这样的方法,我想避免膨胀DataObject。尽管如此,我还是很感兴趣是否有一种好的解决方案,而不需要在DataObject中实现所有方法。 - MrToast
好的,作为妥协,我可以使用扩展方法。这不会使DataObject膨胀,而且我能够保持我的代码干净和清晰。 - MrToast
@ooorndtski:如果DataObject太大了,那么似乎是DataObject的设计问题。是否有可能从DataObject中提取一些类? - Dmitry Bychenko
1
@ooorndtski:当您无法更改目标对象(通常是标准库之一,例如string)时,扩展方法是一种良好的实践。如果您实际上可以重新设计DataObject,则实现扩展方法只会带来不必要的复杂性。 - Dmitry Bychenko
好的,非常感谢。经过思考你的帖子后,我决定重新设计我的应用程序。修改DataObject实例的方法已添加到DataObject中。 - MrToast
@ooorndtski:尽量遵循单一职责原则(请参考SOLID - https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)); DataObject 应该包含数据及其修改的方法(包括操作)。 - Dmitry Bychenko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接