在哪里以及如何申请锁定

时间:2011-03-08 14:46:58

标签: c# multithreading concurrency

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

谢谢。

4 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

有一个很好的单例实现和优化here的写法(你确定菜单是一个单例 - 对于所有用户来说它是相同的菜单吗?)

由于您希望仅针对写入进行优化,您可能还需要查看ReaderWriterLockSlim

答案 3 :(得分:0)

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

private object _sync = new Object();

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

如果在更改数据的代码中使用lock,则还需要在读取数据的所有代码中使用锁定,否则锁定无效。

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