C# 在哪里以及如何应用锁
我试图找出将锁定应用于以下类的正确方法。简而言之,该对象是一个单例对象,在创建时,会根据给定目录中的xml文件构建数量可变的菜单。现在,只允许读取,因此不会发生锁定(MSDN状态读取在字典中是线程安全的)。但是,我还安装了一个文件系统监视程序,以便在发生更改时重新构建菜单。有两个字典可以阅读,所以我需要一种方法来处理这个问题。我可以用锁(这个),但有更好的方法吗?因此,我唯一想冻结读取的时间是在更新发生时(查看ctor) 下面是一个视觉课程:C# 在哪里以及如何应用锁,c#,multithreading,concurrency,C#,Multithreading,Concurrency,我试图找出将锁定应用于以下类的正确方法。简而言之,该对象是一个单例对象,在创建时,会根据给定目录中的xml文件构建数量可变的菜单。现在,只允许读取,因此不会发生锁定(MSDN状态读取在字典中是线程安全的)。但是,我还安装了一个文件系统监视程序,以便在发生更改时重新构建菜单。有两个字典可以阅读,所以我需要一种方法来处理这个问题。我可以用锁(这个),但有更好的方法吗?因此,我唯一想冻结读取的时间是在更新发生时(查看ctor) 下面是一个视觉课程: public class XmlMenuProvid
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);
}
}
公共类XmlMenuProvider:IMenuProvider{
私有只读的INavigationService导航;
专用只读词典菜单;
私有只读字典菜单查找;
专用只读文件系统监视器;
公共XmlMenuProvider(字符串折叠路径,INavigationService导航)
{
这个.导航=导航;
this.menusLookup=新字典();
this.menus=LoadFromSourceDirectory(folderPath);
this.monitor.Changed+=(o,e)=>{
//TODO-添加锁定
};
}
公共IEnumerable GetMenuItems(字符串名称){
返回菜单[名称];
}
公共菜单项FindItemByName(字符串菜单,字符串名称){
返回菜单查找[菜单][名称];
}
私有字典LoadFromSourceDirectory(字符串folderPath){
var菜单=新字典();
foreach(目录.GetFiles(folderPath,*.xml)中的var文件){
var root=XDocument.Load(file.Elements().First();
var name=root.Attribute(“name”).Value;
var lookup=newdictionary();
menusLookup.Add(名称、查找);
添加(名称,BuildMenuHiearchyFromElement(根,查找,空));
}
返回菜单;
}
私有IEnumerable BuildMenuHiearchyFromElement(XElement元素、字典查找、MenuItem父级){
返回元素。元素(“项”)
.选择(e=>{
var mi=CreateMenuItemFromElement(e,lookup,parent);
lookup.Add(mi.Name,mi);
返回mi;
}
).ToList();
}
私有MenuItem CreateMnuItemFromElement(XElement元素、字典查找、MenuItem父级){
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){
角色=新字符串[]{};
}
var attrClick=element.Attribute(“IsClickable”);
如果(单击!=null){
isClickable=bool.Parse(attrClick.Value);
}
var navigateUrl=string.Empty;
如果(可点击){
navigateUrl=navigation.FetchDestination(名称);
}
返回新的MenuItem(名称、显示、导航、可点击、角色、x=>BuildMenuHiearchyFromElement(元素、查找、x)、父级);
}
}
谢谢。我认为您可能希望使用更新程序例程在开始时声明的锁,并在完成时释放的锁。您不应该使用锁(这个),而应该创建一个锁对象(类中的私有对象的距离)并锁定该对象,而不是此对象。单例实现和优化有一个很好的总结(您确定菜单是单例菜单吗?所有用户的菜单都是相同的吗?)
由于您希望仅对写入操作进行锁定优化,您可能还希望查看通常建议创建用于锁定的私有对象:
private object _sync = new Object();
通过使用私有对象作为标识符,只有类中的代码可以访问它,因此类外的任何代码都不会使用相同的标识符锁定并导致死锁
如果在更改数据的代码中使用锁,则还需要在读取数据的所有代码中使用锁,否则该锁将无效
请注意,锁不会以任何方式保护数据,它只是一种确保每次只有一个线程进入代码部分的方法。是的,但我还需要锁定执行读取否的两个方法??这正是我在重新构建菜单时只想锁定的地方。Lock(这个)可以解决这个问题。@Marco-如果不锁定
Lock()
中提到的实例,则锁定该实例。差别很大@Felice-为什么从不lock(this)
?lock
在保护静态时应该在静态上,在保护实例时应该在实例上。@Michael锁定这可能会导致死锁,因为任何人都可以锁定它,即使是在外部。