Создание (N) каталогов по определенному пути в цикле, который резко останавливается - PullRequest
1 голос
/ 25 марта 2019

Я пытаюсь создать 10000 папок в определенном каталоге.У меня есть алгоритм для создания случайных имен для каждой папки.

Проблема, с которой я сталкиваюсь, заключается в том, что когда я запускаю этот код, он останавливается с созданием около 21 каталога.MessageBox.Show() отладка, однако она никогда не появляется.

this.WorkingDir - это строковое свойство, которое выглядит следующим образом.

"C:\\Users\\Reapism\\Desktop\\Yo\\"

        DirectoryInfo directoryInfo;
        string dirName; // directory name
        const int dirNameLength = 15;

        for (int i = 0; i < 10000; i++) {
            dirName = GetRandomString(dirNameLength); // generates "unique name"
            try {
                directoryInfo = Directory.CreateDirectory(this.WorkingDir + dirName);
            } catch (Exception e) {
                MessageBox.Show($"{i} {dirName} failed. {e.ToString()}");
            }
        }

        // Inserting a breakpoint here yields 21 directories

        DirectoryInfo d = new DirectoryInfo(this.WorkingDir);
        DirectoryInfo[] directories = d.GetDirectories($"*");

        foreach (DirectoryInfo dir in directories) {
            try {
                Directory.Delete(dir.FullName);
            } catch (Exception e) {
                throw new FileNotFoundException($"Error deleting the directory!" + e.ToString());
            }
        }

Интересная вещь - это когда я используюОтладчик, и проходите каждую итерацию цикла for при создании каталогов, он идет дальше, чем каталоги 21 , и, вероятно, проходит до 10 тыс.Это привело бы меня к мысли, что существует предел для создания каталогов за определенное количество времени.

Что беспокоит, так это то, что исключение не выдается, цикл просто прерывается.

Связано ли это с тем, что процессор работает быстрее, чем диск может сообщить или записать новую папку?Если так, как я могу обойти это?Эта функция является временной функцией, и использование Thread.Sleep(50), например, не может быть использовано.

EDIT GetRandomString () была проблема.Ниже приведен код неработающего GetRandomString (), как мы можем это исправить.Мы всегда передаем постоянное целое число в его аргумент.Связано ли это с тем, что Random создает такое же начальное число?

    /// <summary>
    /// Generates a random string that may contain
    /// [a-z], [A-Z], and [0-9]. (62 characters.)
    /// </summary>
    /// <param name="length">The length of the string to generate</param>
    /// <returns></returns>
    /// <exception cref="ArgumentOutOfRangeException"></exception>

    private string GetRandomString(int length) {
        Random rnd = new Random();
        string rndStr = string.Empty;

        if (length < 1) {
            throw new ArgumentOutOfRangeException("Length must be greater than 0!");
        }

        for (int i = 0; i < length; i++) {
            rndStr += this.charList[rnd.Next(this.charList.Length)];
        }

        return rndStr;
    }

Если вы хотите увидеть, получаете ли вы результаты, аналогичные мне или другим, скопируйте и вставьте код в вашу IDE.Обратите внимание, я использую WPF и CLR v4.0.30319

Использование операторов:

using System;

using System.IO;

using System.Windows.Forms;

Ответы [ 3 ]

2 голосов
/ 26 марта 2019

Относительно обновления для GetRandomString() ...

Когда вы создаете новый экземпляр Random, не передав ему начальное число, конструктор по умолчанию будет заполнять новый экземпляр изEnvironment.TickCount - время в миллисекундах с момента запуска компьютера.Поскольку он имеет разрешение 1 мс, ваш код, скорее всего, создает множество Random экземпляров с одинаковым начальным значением.Это означает, что метод будет возвращать одни и те же значения каждый раз, пока Environment.TickCount не перейдет к следующему значению.

Чтобы решить эту проблему, вы должны использовать один экземпляр Random и использовать его во всех вызовах.Нечто подобное должно работать:

private Random _rnd = null;

private string GetRandomString(int length) 
{
    if (_rnd == null)
        _rnd = new Random();

    if (length < 1) 
        throw new ArgumentOutOfRangeException("Length must be greater than 0!");

    var sb = new StringBuilder(length);
    for (int i = 0; i < length; i++)
        sb.Append(charList[_rnd.Next(charList.Length)]);
    return sb.ToString();
}

По сути, это то же самое, что у вас уже есть, но создает только один экземпляр Random.И избегает всех этих неприятных композиций строк, которые у вас есть, так что это будет немного быстрее и более дружественным по отношению к памяти.

Другой метод, если вы не заботитесь о реальных именах, - этоиспользуйте Guid.NewGuid() для создания гарантированных уникальных значений.

1 голос
/ 25 марта 2019

Ваша функция GetRandomString генерирует неуникальные имена, потому что вы каждый раз создаете новое Random.Вы можете использовать закрытый член класса Random, который был создан один раз с экземпляром класса.Я написал образец подсчета уникальных имен.

string[] dirNames = new string[10000];

for (i = 0; i < 10000; ++i)
    dirNames[i] = GetRandomString(dirNameLength); // generates "unique name";

foreach (var dr in dirNames.GroupBy(x => x).Select(x => new { Name = x.Key, Count = x.Count() }).Where(x => x.Count > 1))
{
    Console.WriteLine($"{dr.Count} {dr.Name}");
}

Попробуйте это.И не забывайте, что имена файлов не чувствительны к регистру, поэтому вы должны использовать только 36 символов, а не 62.

private static readonly char[] charList = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
private static readonly Random _random = new Random();

private static string GetRandomString(int length)
{
    if (length < 1)
        throw new ArgumentOutOfRangeException("Length must be greater than 0!");

    return new string(Enumerable.Repeat(charList, length).Select(s => s[_random.Next(s.Length)]).ToArray());
}
1 голос
/ 25 марта 2019

Вероятно, ваша функция GetRandomString выдает исключение при выполнении в реальном времени. Поместите это в блок try и проверьте. Я попытался создать 10000 папок с именами 1,2,3 ... 10000, и я создал все.

    for (int i = 0; i < 10000; ++i)
    {
        Directory.CreateDirectory(Path.Combine(WorkingDir, $"{i}"));
    }
...