再会!
我正在编写一个程序,我需要在其中扫描整个注册表。我决定递归地进行,就像我对文件和文件夹所做的那样,它们被扫描了大约 20 秒。结果,以下代码出现在某处:
class AllRegistryPermissions
{
public Dictionary<string, StringDictionary> HKCRKeys;
public Dictionary<string, StringDictionary> HKCUKeys;
public Dictionary<string, StringDictionary> HKLMKeys;
static List<string> FPK;
static public void GetRegistry(Dictionary<string, StringDictionary> Keys, RegistryKey ParentKey)
{
string Name = ParentKey.Name;
if (!((Properties.Settings.Default.RFilterPathList.Contains(Name)) || (FPK.Any(t => Name.Contains(t))))) //фильтрация по пути и по ключевым словам
{
if (ParentKey.ValueCount != 0)
{
Keys.Add(ParentKey.Name, new StringDictionary());
foreach (string name in ParentKey.GetValueNames())
{
Keys[ParentKey.Name].Add(name, ParentKey.GetValue(name, "!!! Cant identify value !!!").ToString());
}
}
else Keys.Add(ParentKey.Name, null);
if (ParentKey.SubKeyCount != 0)
{
foreach (string name in ParentKey.GetSubKeyNames())
{
try
{
if (ParentKey.OpenSubKey(name) != null) GetRegistry(Keys, ParentKey.OpenSubKey(name));
}
catch (System.Security.SecurityException) { }
}
}
}
}
public AllRegistryPermissions()
{
FPK = Properties.Settings.Default.RFilterKeywordList.Cast<string>().ToList();
HKCRKeys = new Dictionary<string, StringDictionary>();
GetRegistry(HKCRKeys, Registry.ClassesRoot);
HKCUKeys = new Dictionary<string, StringDictionary>();
GetRegistry(HKCUKeys, Registry.CurrentUser);
HKLMKeys = new Dictionary<string, StringDictionary>();
GetRegistry(HKLMKeys, Registry.LocalMachine);
}
}
他扫描正常,但仅扫描一根树枝就HKCR需要五分钟。结果显然不适合我(因为同一个 Systracer 会在 20-30 秒内扫描所有内容),我考虑了多线程。经过长时间的巫术(我想我已经从一个heowolder至少成长为一个bydlocker),代码开始看起来像这样:
class AllRegistryPermissions
{
public ConcurrentDictionary<string, StringDictionary> HKCRKeys;
public ConcurrentDictionary<string, StringDictionary> HKCUKeys;
public ConcurrentDictionary<string, StringDictionary> HKLMKeys;
static CountdownEvent ce;
static int maxtasks = 0;
static int cantopen = 0;
static int cantopensec = 0;
static List<string> FPK;
static public void GetRegistry(ConcurrentDictionary<string, StringDictionary> Keys, RegistryKey ParentKey)
{
string Name = ParentKey.Name;
if (!((Properties.Settings.Default.RFilterPathList.Contains(Name)) || (FPK.Any(t => Name.Contains(t)))))
{
int close = 0;
if (ParentKey.ValueCount != 0)
{
ce.AddCount();
if (ce.CurrentCount > maxtasks) maxtasks = ce.CurrentCount;
Task.Run(() =>
{
Keys.GetOrAdd(ParentKey.Name, new StringDictionary());
foreach (string name in ParentKey.GetValueNames())
{
Keys[ParentKey.Name].Add(name, ParentKey.GetValue(name, "!!! Cant identify value !!!").ToString());
}
close++;
if (close == 2) ParentKey.Close();
ce.Signal();
});
}
else
{
Keys.GetOrAdd(ParentKey.Name, (StringDictionary)null);
close++;
}
if (ParentKey.SubKeyCount != 0)
{
ce.AddCount();
if (ce.CurrentCount > maxtasks) maxtasks = ce.CurrentCount;
Task.Run(() =>
{
foreach (string name in ParentKey.GetSubKeyNames())
{
try { if (ParentKey.OpenSubKey(name) != null) GetRegistry(Keys, ParentKey.OpenSubKey(name));
else cantopen++; }
catch (System.Security.SecurityException) {
cantopensec++; }
}
close++;
if (close == 2) ParentKey.Close();
ce.Signal();
});
}
else close++;
}
}
public AllRegistryPermissions()
{
FPK = Properties.Settings.Default.RFilterKeywordList.Cast<string>().ToList();
ce = new CountdownEvent(3);
Task hkcrTask = Task.Run(() =>
{
HKCRKeys = new ConcurrentDictionary<string, StringDictionary>();
GetRegistry(HKCRKeys, Registry.ClassesRoot);
ce.Signal();
});
Task hkcuTask = Task.Run(() =>
{
HKCUKeys = new ConcurrentDictionary<string, StringDictionary>();
GetRegistry(HKCUKeys, Registry.CurrentUser);
ce.Signal();
});
Task hklmTask = Task.Run(() =>
{
HKLMKeys = new ConcurrentDictionary<string, StringDictionary>();
GetRegistry(HKLMKeys, Registry.LocalMachine);
ce.Signal();
});
ce.Wait();
Console.WriteLine("maximal tasks numbers: " + maxtasks);
Console.WriteLine("can't open {0} subkeys", cantopen);
Console.WriteLine("can't open {0} subkeys sec", cantopensec);
}
}
一切都会好起来的,但我觉得这一点都不好,也不对。我是第一次使用多线程,我不知道一次正常运行多少线程。谷歌搜索一点看到1-4。我的代码最多同时运行了130,000 个线程,我怀疑这非常糟糕(我以为计算机会爆炸)。但设计师在一分钟内完成了工作。
问题是,我该如何解决这个问题?不知何故重新编写代码,调节线程数,改变方法本身或用另一种语言重写代码(我听说C ++在这方面更快)?
该程序将使用该类的两个实例来比较并找出哪些键已被删除、添加或更改。
关于您的代码:
try catch,但这还不够。仔细阅读文档。RegistryKey.OpenSubKey这是一个 api 调用的包装器,它相应地分配了一些 OS 对象。而这个对象在使用后需要释放。因此,类型RegistryKey为IDisposable。您不仅不免费RegistryKey,而且还OpenSubKey两次调用合同。工作代码示例:
阅读课:
使用示例:
关于速度:
我测试了您的代码和我的代码,它们在速度方面大致相同。平均而言,单线程模式读取我需要 10-20 秒;在多线程模式下,由于我的HKEY_LOCAL_MACHIN分支很大,其余的很小,因此速度并没有增加太多。
总的来说,我没有像你那样达到如此糟糕的指标。我用谷歌搜索了一下,发现了这个和这个。简而言之...
关于 C++:
加号当然是一种强大的语言,通常它上面的代码会更快。但是您对操作系统功能的调用非常密集,而且很明显,优点不会在速度上产生任何大的差异。此外,复杂的语言和用它写作的优势,那么你已经写了,会花费你更多的时间和精力,而且收益可能会是一分钱。