Доброго времени суток!
Пишу программу, в которой мне нужно просканировать весь реестр. Решил делать это рекурсивно, как делал с файлами и папками, их сканировало около 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 секунд) и я задумался о многопоточности. После долгого колдовства (хотелось бы считать, что я перерос из хэоуволдщика хотя бы в быдлокодера) код стал выглядеть так:
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. Мой код одновременно запускал максимум 130000 потоков и я подозреваю, что это очень не хорошо (я думал компьютер взорвется). Но конструктор отработал за минуту. Собственно вопрос, как мне решить проблему? Как-то переделывать код, регулировать количество потоков, менять сам подход или переписывать код на другом языке (слышал, что C++ шустрее в этом плане)? В программе два экземпляра этого класса будут испрользоваться, чтобы сравнить и обнаружить, какие ключи были удалены, добавлены или изменены.
Касаемо вашего кода:
try catch
, но этого не достаточно. Читаете
внимательно документацию.RegistryKey.OpenSubKey
это обертка над апишным вызовом, который
соответственно выделяет некоторый объект ОС. И этот объект нужно
после использования освобождать. По этому тип RegistryKey
является
IDisposable
. Вы мало того что не освобождаете RegistryKey
, вы
вдобавок вызываете OpenSubKey
два раза подрядят.Пример рабочего кода:
Класс для чтения:
class RegData
{
public Dictionary<string, StringDictionary> HKCRKeys;
public Dictionary<string, StringDictionary> HKCUKeys;
public Dictionary<string, StringDictionary> HKLMKeys;
public RegData()
{
HKLMKeys = new Dictionary<string, StringDictionary>();
HKCRKeys = new Dictionary<string, StringDictionary>();
HKCUKeys = new Dictionary<string, StringDictionary>();
}
public void ReadSingleThread()
{
Clear();
var FPK = Properties.Settings.Default.RFilterPathList.Cast<string>().ToList();
//GetRegistry(HKCRKeys, Registry.ClassesRoot, FPK);
GetRegistry(HKCUKeys, Registry.CurrentUser, FPK);
GetRegistry(HKLMKeys, Registry.LocalMachine, FPK);
}
public void ReadMultiThread()
{
Clear();
var FPK = Properties.Settings.Default.RFilterPathList.Cast<string>().ToList();
//var hkcrThread = RunThread(HKCRKeys, Registry.ClassesRoot, FPK);
var hkcuThread = RunThread(HKCUKeys, Registry.CurrentUser, FPK);
var hklmThread = RunThread(HKLMKeys, Registry.LocalMachine, FPK);
//hkcrThread.Join();
hkcuThread.Join();
hklmThread.Join();
}
private void Clear()
{
HKLMKeys.Clear();
HKCRKeys.Clear();
HKCUKeys.Clear();
}
private Thread RunThread(Dictionary<string, StringDictionary> keys, RegistryKey parentKey, List<string> FPK)
{
var thread = new Thread(() => {
try {
GetRegistry(keys, parentKey, FPK);
}
catch (Exception e) {
Console.WriteLine($"Thread proc for {parentKey.Name} fault: {e.Message}");
}
});
thread.Start();
return thread;
}
//###########
//
// Static
//
//###########
static private void GetRegistry(Dictionary<string, StringDictionary> keys, RegistryKey parentKey, List<string> FPK)
{
string Name = parentKey.Name;
if (Properties.Settings.Default.RFilterPathList.Contains(Name) || FPK.Any(t => Name.Contains(t))) {
return;
}
AddValues(keys, parentKey);
AddSubKeys(keys, parentKey, FPK);
}
static private void AddValues(Dictionary<string, StringDictionary> keys, RegistryKey parentKey)
{
string[] valueNames = null;
if (!TryGetValueNames(parentKey, out valueNames)) {
keys.Add(parentKey.Name, null);
return;
}
var values = new StringDictionary();
keys.Add(parentKey.Name, values);
foreach (string name in valueNames) {
values.Add(name, parentKey.GetValue(name, "error value").ToString());
}
}
static private void AddSubKeys(Dictionary<string, StringDictionary> keys, RegistryKey parentKey, List<string> FPK)
{
string[] subKeyNames = null;
if (!TryGetSubKeyNames(parentKey, out subKeyNames)) {
return;
}
foreach (string subKeyName in subKeyNames) {
RegistryKey subkey = null;
if (TryOpenSubKey(parentKey, subKeyName, out subkey)) {
using (subkey) {
GetRegistry(keys, subkey, FPK);
}
}
}
}
static private bool TryGetValueNames(RegistryKey parentKey, out string[] valueNames)
{
valueNames = null;
try {
if (parentKey.ValueCount != 0) {
valueNames = parentKey.GetValueNames();
}
}
catch (Exception e) {
Console.WriteLine($"GetValueNames of {parentKey.Name} fail: {e.Message}");
}
return valueNames != null;
}
static private bool TryGetSubKeyNames(RegistryKey parentKey, out string[] subKeyNames)
{
subKeyNames = null;
try {
if (parentKey.SubKeyCount != 0) {
subKeyNames = parentKey.GetSubKeyNames();
}
}
catch (Exception e) {
Console.WriteLine($"GetSubKeyNames of {parentKey.Name} fail: {e.Message}");
}
return subKeyNames != null;
}
static private bool TryOpenSubKey(RegistryKey parentKey, string subKeyName, out RegistryKey subKey)
{
subKey = null;
try {
subKey = parentKey.OpenSubKey(subKeyName);
if (subKey == null) {
Console.WriteLine($"OpenSubKey({subKeyName}) of {parentKey.Name} generic fail");
}
}
catch (System.Security.SecurityException) {
// антиспам
}
catch (Exception e) {
Console.WriteLine($"OpenSubKey({subKeyName}) of {parentKey.Name} fail: {e.Message}");
}
return subKey != null;
}
}
Пример использования:
static void Main(string[] args)
{
TestSingleThread();
TestMultiThread();
TestSingleThread();
TestMultiThread();
TestSingleThread();
TestMultiThread();
Console.ReadKey();
}
static void TestSingleThread()
{
var sw = new Stopwatch();
sw.Start();
var t = new RegData();
t.ReadSingleThread();
sw.Stop();
Console.WriteLine($"Singlethread done: {sw.ElapsedMilliseconds}");
}
static void TestMultiThread()
{
var sw = new Stopwatch();
sw.Start();
var t = new RegData();
t.ReadMultiThread();
sw.Stop();
Console.WriteLine($"Multithread done: {sw.ElapsedMilliseconds}");
}
Касаемо скорости:
Я тестировал ваш код и свой, и они по скорости были примерно одинаковые. У меня в среднем чтение в одно-поточном режиме занимало 10-20 секунд, в много-поточном скорость не сильно увеличивается за счет того что у меня ветка HKEY_LOCAL_MACHIN очень большая, а остальнные маленькие.
Во общем мне ни как не получилось достич таких плохих показателей как у вас. Я маленько погуглил и нашел это и это. Если кратко то...
Касаемо С++:
Плюсы конечно мощный язык и в целом код на нем будет быстрее. Но у вас идет очень интенсивный вызов функций ОС, и тут что шарп, что плюсы не дадут каких то больших разниц в скоростях. Вдобавок плюсы сложный язык и написать на нем, то вы уже написали займет у вас намного больше времени и сил, а выгода возможно буде копеечной.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Мне необходимо распарсить слайды презентации PowerPoint (pptx), а именно получить три значения для каждого элемента (shape) на слайде:
Пытаюсь вывести список с категориями, первая категория выводится, а последующие нет
Доброго времени сутокВозникла проблема с привязкой структуры к DataGrid, раньше это делал, но вот сейчас ничего не получается, подскажите в чем...