【问题标题】:Where and How to apply lock在哪里以及如何应用锁
【发布时间】:2023-04-02 00:51:01
【问题描述】:

我正在尝试找出将锁定应用于以下类的正确方法。简而言之,该对象是一个单例对象,当创建时,它会从给定目录中的 xml 文件构建可变数量的菜单。现在,只允许读取,因此不会发生锁定(MSDN 状态读取是字典中的线程安全的)。但是,我还连接了一个文件系统观察程序,以便在发生更改时重新构建菜单。有两个字典发生读取,所以我需要一种方法来处理这个问题。我可以使用 Lock(this),但有更好的方法吗?所以,我唯一想冻结读取的时间是更新发生时(查看 ctor)。

这是一个视觉类:

public class XmlMenuProvider : IMenuProvider {
    private readonly INavigationService navigation;
    private readonly Dictionary<string, IEnumerable<MenuItem>> menus;
    private readonly Dictionary<string, Dictionary<string, MenuItem>> menusLookup;
    private readonly FileSystemWatcher monitor;

    public XmlMenuProvider(string folderPath, INavigationService navigation)
    {
        this.navigation = navigation;
        this.menusLookup = new Dictionary<string, Dictionary<string, MenuItem>>();
        this.menus = LoadFromSourceDirectory(folderPath);
        this.monitor.Changed += (o, e) => {
                // TODO - Add Locking
            };
    }

    public IEnumerable<MenuItem> GetMenuItems(string name) {
        return menus[name];
    }

    public MenuItem FindItemByName(string menu, string name) {
        return menusLookup[menu][name];
    }

    private Dictionary<string, IEnumerable<MenuItem>> LoadFromSourceDirectory(string folderPath) {
        var menus = new Dictionary<string, IEnumerable<MenuItem>>();
        foreach (var file in Directory.GetFiles(folderPath, "*.xml")) {
            var root = XDocument.Load(file).Elements().First();
            var name = root.Attribute("name").Value;

            var lookup = new Dictionary<string, MenuItem>();
            menusLookup.Add(name, lookup);
            menus.Add(name, BuildMenuHiearchyFromElement(root, lookup, null));
        }
        return menus;
    }

    private IEnumerable<MenuItem> BuildMenuHiearchyFromElement(XElement element, Dictionary<string, MenuItem> lookup, MenuItem parent) {
        return element.Elements("Item")
                      .Select(e => {
                          var mi = CreateMenuItemFromElement(e, lookup, parent);
                          lookup.Add(mi.Name, mi);
                          return mi;
                      }
                ).ToList();
    }

    private MenuItem CreateMenuItemFromElement(XElement element, Dictionary<string, MenuItem> lookup, MenuItem parent) {
        var name = element.Attribute("Name").Value;
        var display = element.Attribute("DisplayName").Value;
        var isClickable = true;

        var roles = element.Attribute("Roles").Value.Split(',');
        if (roles.Length == 1 && roles.First() == string.Empty) {
            roles = new string[] { };
        }
        var attrClick = element.Attribute("IsClickable");
        if (attrClick != null) {
            isClickable = bool.Parse(attrClick.Value);
        }
        var navigateUrl = string.Empty;
        if (isClickable) {
            navigateUrl = navigation.FetchDestination(name);
        }

        return new MenuItem(name, display, navigateUrl, isClickable, roles, x => BuildMenuHiearchyFromElement(element, lookup, x), parent);
    }
}

谢谢。

【问题讨论】:

    标签: c# multithreading concurrency


    【解决方案1】:

    你不应该使用 lock(this) 而是创建一个 lockObject (对象私有到类中的一个实例)并锁定该对象而不是 this。

    【讨论】:

    • 是的,但是我还需要锁定执行读取的两种方法不??这正是我在重建菜单时只想锁定的地方。 Lock(this) 可以解决这个问题。
    • @Marco - 您不锁定lock() 中提到的 实例,而是锁定 实例。很大的区别。 @Felice - 为什么从不lock(this)
    • lock 在保护静态时应该在静态上,在保护实例时应该在实例上。
    • @Michael 锁定它会导致死锁,因为任何人都可以锁定它,即使是外部的。
    【解决方案2】:

    有一篇很好的关于单例实现和优化here 的文章(你确定菜单是单例的吗?它对所有用户来说都是同一个菜单吗?)

    由于您只想通过锁定来优化写入,您可能还想查看ReaderWriterLockSlim

    【讨论】:

      【解决方案3】:

      我认为您可能希望使用mutex,而不是锁定,更新程序例程在开始时声明并在完成时释放。

      【讨论】:

        【解决方案4】:

        通常建议创建一个用于锁定的私有对象:

        private object _sync = new Object();
        

        通过使用私有对象作为标识符,只有类中的代码可以访问它,因此类外的任何代码都不会使用相同的标识符锁定并导致死锁。

        如果在更改数据的代码中使用了锁,那么在所有读取数据的代码中也需要使用锁,否则锁是没有用的。

        请注意,锁不会以任何方式保护数据,它只是确保一次只有一个线程进入一段代码的一种方式。

        【讨论】:

          猜你喜欢
          • 2013-01-29
          • 1970-01-01
          • 2022-10-18
          • 2020-08-30
          • 2021-09-01
          • 2018-03-22
          • 2020-03-19
          • 2011-09-04
          • 2017-10-05
          相关资源
          最近更新 更多