在Task中迭代Directory.GetFiles阻止UI线程

时间:2017-10-22 20:46:53

标签: c# multithreading asynchronous

我正在尝试获取文件列表并在异步任务中迭代它们,以便UI线程可以自由更新输出窗口(或进度条)。这只是为了释放UI线程。当我使用普通的string[] var files = new[] {"test path"});时,效果很好 我还要注意,我已将此代码的示例与Directory.GetFiles()一起删除。但是,每当我引入DirectoryDirecoryInfo时,UI线程都会被阻止。无论我是在任务内部还是外部检索文件,结果都是一样的。

为什么此代码会阻止UI线程?我不一定需要异步迭代文件(这些是我在搜索主题时找到的唯一类型的答案)。我只需要获取文件列表并在异步上下文中迭代它们。

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace Indexer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private CancellationTokenSource _stopWorkingCts;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            if (_stopWorkingCts == null || _stopWorkingCts.IsCancellationRequested)
            {
                _stopWorkingCts = new CancellationTokenSource();

                AnalyzeFiles(this);
            }
        }

        private void AnalyzeFiles(MainWindow mainWindow)
        {
            UpdateWindow("Analyzing files.");

            Task.Run(() =>
            {
                var files = Directory.GetFiles(_path, "*.dat", SearchOption.TopDirectoryOnly);
                foreach (var dir in files)
                {
                    if (!_stopWorkingCts.IsCancellationRequested)
                    {
                        mainWindow.UpdateWindow($"test {dir}");
                    }
                    else
                    {
                        break;
                    }
                }
            });
        }

        private void StopButton_Click(object sender, RoutedEventArgs e)
        {
            UpdateWindow("Connection closed.", false);

            if (_stopWorkingCts != null)
            {
                _stopWorkingCts.Cancel();
                UpdateWindow($"Stop requested.", false);
            }
        }

        private void UpdateWindow(string text, bool queueMessage = false)
        {
            Dispatcher.BeginInvoke(new Action(() =>
            {
                if (queueMessage)
                {
                    newOutputLines.Add($"{DateTime.Now} - {text}");

                    if (newOutputLines.Count >= 5)
                    {
                        OutputBox.AppendText(string.Join(Environment.NewLine, newOutputLines.ToArray()) +
                                             Environment.NewLine);
                        OutputBox.ScrollToEnd();

                        newOutputLines.Clear();
                    }
                }
                else
                {
                    OutputBox.AppendText(text + Environment.NewLine);
                    OutputBox.ScrollToEnd();
                }

                if (OutputBox.LineCount >= 500)
                {
                    OutputBox.Text =
                        string.Join(Environment.NewLine,
                            OutputBox.Text.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
                                .Skip(Math.Max(0, OutputBox.LineCount - 500)));
                }
            }));
        }
    }
}

1 个答案:

答案 0 :(得分:1)

请参阅Directory.GetFiles()

  

...当您使用GetFiles时,您必须等待整个名称数组   在您可以访问阵列之前返回。

所以GetFiles()是一个阻塞调用;只需将内部移动到您的任务中即可。

变化:

        var files = Directory.GetFiles(_path, "*.dat", SearchOption.TopDirectoryOnly);

        Task.Run(() =>
        {
            // ... rest of the code ...

要:

        Task.Run(() =>
        {
            var files = Directory.GetFiles(_path, "*.dat", SearchOption.TopDirectoryOnly);
            // ... rest of the code ...