在何处以及如何应用锁

发布于 2024-10-20 15:07:17 字数 3177 浏览 2 评论 0原文

我试图找出将锁定应用于以下课程的正确方法。简而言之,该对象是一个单例,创建时会根据给定目录中的 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);
    }
}

谢谢。

I'm trying to figure out the correct way of applying locking to the following class. In a nutshell, the object is a singleton and when created, builds a variable number of menus from xml files in a given directory. Right now, only reads are allowed thus no locking takes places (MSDN states reads are thread-safe from dictionary). But, I also have a file system watcher wired up so I can re-build menus when a change takes place. There are two dictionaries where reads occur and so I need a way to handle this. I could use Lock(this), but is there a better way? So, the only time I want to freeze reads is when the updating takes place (look in ctor).

Here is the class for a visual:

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);
    }
}

Thank You.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

简单 2024-10-27 15:07:17

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

You should never use lock(this) but create a lockObject instead ( an istance of object private into the class ) and lock on that object instead of this.

迷爱 2024-10-27 15:07:17

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

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

There is a good write up of Singleton implementations and optimisations here (are you sure that the menu is a singleton - is it the same menu for all users?)

Since you want to optimise with locking only on the writes, you might also want to look at ReaderWriterLockSlim

掩饰不了的爱 2024-10-27 15:07:17

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

Rather than lock, I think you might want to use a mutex that the updater routine claims when it begins and releases when it's finished.

哀由 2024-10-27 15:07:17

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

private object _sync = new Object();

通过使用私有对象作为标识符,只有类中的代码可以访问它,因此不存在类外部的任何代码可以使用相同标识符进行锁定的风险,并且造成僵局。

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

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

It's commonly recommended to create a private object that you use for locking:

private object _sync = new Object();

By using a private object as identifier, only the code in the class can access it, so there is no risk that any code outside the class could lock using the same identifier and cause a deadlock.

If you use lock in the code that changes the data, you also need to use a lock in all code that reads the data, otherwise the lock is useless.

Note that the lock doesn't protect the data in any way, it's only a way to make sure that only one thread at a time enters a section of the code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文