Async Await - нужно руководство

Я пробовал много разных способов, чтобы заставить это работать, и я уверен, что это не правильный способ подключить асинхронное / ожидание для многопоточности. Вот что у меня так далеко. Это обходчик каталогов, который я попытался сделать асинхронным. Я знаю, что вы не видите никаких асинхронных или ожидающих ключевых слов, и это потому, что я потерпел неудачу, но это то, что я пытаюсь сделать. Прямо сейчас он работает в консольном приложении, но позже я получу абстракцию и рефакторинг, когда получу работающий POC. Любое руководство приветствуется.

    static void RunProgram(CancellationToken ct)
    {
        try
        {
            foreach (var dir in _directoriesToProcess)
            {
                var newTask = CreateNewTask(dir, ct);
                _tasks.Add(newTask);
            }

            while (_tasks.Count > 0)
            {
                lock (_collectionLock)
                {
                    var t = _tasks.Where(x => x.IsCompleted == true).ToList();
                    if (t != null)
                        foreach (var task in t)
                        {
                            _tasks.Remove(task);
                        }
                }
            }

            OutputFiles();
            StopAndCleanup();
        }
        catch (Exception ex)
        {
            Log(LogColor.Red, "Error: " + ex.Message, false);
            _cts.Cancel();
        }
    }


    static Task CreateNewTask(string Path, CancellationToken ct)
    {
        return Task.Factory.StartNew(() => GetDirectoryFiles(Path, ct), ct);
    }

    static void GetDirectoryFiles(string Path, CancellationToken ct)
    {
        if (!ct.IsCancellationRequested)
        {
            List<string> subDirs = new List<string>();
            int currentFileCount = 0;
            try
            {
                currentFileCount = Directory.GetFiles(Path, _fileExtension).Count();
                subDirs = Directory.GetDirectories(Path).ToList();

                lock (_objLock)
                {
                    _overallFileCount += currentFileCount;
                    Log(LogColor.White, "- Current path: " + Path);
                    Log(LogColor.Yellow, "--  Sub directory count: " + subDirs.Count);
                    Log(LogColor.Yellow, "--  File extension: " + _fileExtension);
                    Log(LogColor.Yellow, "--  Current count: " + currentFileCount);
                    Log(LogColor.Red, "--  Running total: " + _overallFileCount);
                    _csvBuilder.Add(string.Format("{0},{1},{2},{3}", Path, subDirs.Count, _fileExtension, currentFileCount));
                    Console.Clear();
                    Log(LogColor.White, "Running file count: " + _overallFileCount, false, true);
                }

                foreach (var dir in subDirs)
                {
                    lock (_collectionLock)
                    {
                        var newTask = CreateNewTask(dir, ct);
                        _tasks.Add(newTask);
                    }
                }
            }
            catch (Exception ex)
            {
                Log(LogColor.Red, "Error: " + ex.Message, false);
                _cts.Cancel();
            }
        }
    }

2 ответа

Решение

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

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

static async Task<IEnumerable<DirectoryStat>> GetDirectoryFiles(string path, string fileExtension, CancellationToken ct)
{
    var thisDirectory = await Task.Run(() => /* Get directory file count and return a DirectoryStat object */);
    var subDirectoriesResults = await Task.WhenAll(Directory.GetDirectories(path).Select(dir => GetDirectoryFiles(dir, fileExtension, ct)));

    return (new[] { thisDirectory }).Concat(subDirectoryResults);
} 

Затем вы можете повторить их позже и извлечь нужные данные из DirectoryStat (и сумма вашего файла рассчитывается в соответствии с _overallFileCount так далее)

ПРИМЕЧАНИЕ: не проверено:)

Вы можете запустить Синхронный Код Асинхронный с Task.Run(() => { //code });Также измените ваш тип возврата на Taskтак что вы можете await это я бы переписал тебе код следующим образом:

static void RunProgram(CancellationToken ct)
{
    try
    {
        foreach (var dir in _directoriesToProcess)
        {
            var newTask = CreateNewTask(dir, ct);
            _tasks.Add(newTask);
        }

        //change your while so it does not execute all the time
        while (_tasks.Count > 0)
        {
            lock (_collectionLock)
            {
                var tsk = _tasks.FirstOrDefault();
                        if (tsk != null)
                        {
                            if (tsk.Status <= TaskStatus.Running)
                                await tsk;
                            _tasks.Remove(tsk);
                        }
            }
        }

        OutputFiles();
        StopAndCleanup();
    }
    catch (Exception ex)
    {
        Log(LogColor.Red, "Error: " + ex.Message, false);
        _cts.Cancel();
    }
}


static Task CreateNewTask(string Path, CancellationToken ct)
{
    return Task.Factory.StartNew(() => GetDirectoryFiles(Path, ct), ct);
}

//always use Task (or Task<T>) as return so you can await the process
static async Task GetDirectoryFiles(string Path, CancellationToken ct)
{
    if (!ct.IsCancellationRequested)
    {
        //Insert Magic
        await Task.Run(() => {
            List<string> subDirs = new List<string>();
            int currentFileCount = 0;
            try
            {
                currentFileCount = Directory.GetFiles(Path, _fileExtension).Count();
                subDirs = Directory.GetDirectories(Path).ToList();

                lock (_objLock)
                {
                    _overallFileCount += currentFileCount;
                    Log(LogColor.White, "- Current path: " + Path);
                    Log(LogColor.Yellow, "--  Sub directory count: " + subDirs.Count);
                    Log(LogColor.Yellow, "--  File extension: " + _fileExtension);
                    Log(LogColor.Yellow, "--  Current count: " + currentFileCount);
                    Log(LogColor.Red, "--  Running total: " + _overallFileCount);
                    _csvBuilder.Add(string.Format("{0},{1},{2},{3}", Path, subDirs.Count, _fileExtension, currentFileCount));
                    Console.Clear();
                    Log(LogColor.White, "Running file count: " + _overallFileCount, false, true);
                }

                foreach (var dir in subDirs)
                {
                    lock (_collectionLock)
                    {
                        var newTask = CreateNewTask(dir, ct);
                        _tasks.Add(newTask);
                    }
                }
            });
        }
        catch (Exception ex)
        {
            Log(LogColor.Red, "Error: " + ex.Message, false);
            _cts.Cancel();
        }
    }
} 
Другие вопросы по тегам