Winforms 递归文件扫描块 UI

Winforms recursive file scan blocks UI

我有一个程序可以搜索给定目录并将所有文件添加到列表视图中。我的问题是 ui 线程在搜索繁忙时卡住了。我试过使用任务,但无法让它异步工作。找到每个文件后必须更新列表视图。

我已经阅读了大量有关 TPL 及其使用方法的资料,但在这种情况下无法正常工作。我让它工作,其中数据处理是在一种创建任务来处理它的方法中进行的。谁能告诉我下面的代码有什么问题以及如何解决?

这是我的代码:

private void button1_Click(object sender, EventArgs e)
{
    Task.Run(() =>
    {
        WalkDirectory(new DirectoryInfo(drive));
    });
}

public void testTaskUpdateLabel(string labelTeks)
{
    Task taskUpdateLabel = new Task(() =>
    {
        label4.Text = labelTeks;
    });
    taskUpdateLabel.Start(uiScheduler);
}

public void testTaskUpdateLabel(string labelTeks)
{
    Task taskUpdateLabel = new Task(() =>
    {
        label4.Text = labelTeks;
    });
    taskUpdateLabel.Start(uiScheduler);
}

public bool WalkDirectory(DirectoryInfo directory)
{
    if (directory == null)
    {
        throw new ArgumentNullException("directory");
    }
    return this.WalkDirectories(directory);
}

private bool WalkDirectories(DirectoryInfo directory)
{
    bool continueScan = true;
    continueScan = WalkFilesInDirectory(directory);

    if (continueScan)
    {
            DirectoryInfo[] subDirectories = directory.GetDirectories();

            foreach (DirectoryInfo subDirectory in subDirectories)
            {
                try
                {
                    if ((subDirectory.Attributes & FileAttributes.ReparsePoint) != 0)
                    {
                        continue;
                    }
                    if (!(continueScan = WalkDirectory(subDirectory)))
                    {
                        break;
                    }
                }
                catch (UnauthorizedAccessException)
                {
                    continue;
                }
            }
    }
    if (continueScan)
    {
        testTaskUpdateLabel(directory.FullName);
    }
    return continueScan;
}

private bool WalkFilesInDirectory(DirectoryInfo directory)
{
    bool continueScan = true;
    // Break up the search pattern in separate patterns
    string[] searchPatterns = _searchPattern.Split(';');

    // Try to find files for each search pattern
    foreach (string searchPattern in searchPatterns)
    {
        if (!continueScan)
        {
            break;
        }
        // Scan all files in the current path
        foreach (FileInfo file in directory.GetFiles(searchPattern))
        {
            try
            {
                testTaskUpdate(file.FullName);
            }
            catch (UnauthorizedAccessException)
            {
                continue;
            }
        }
    }
    return continueScan;           

}

如果您使用 BackgroundWorker class,UI 将起作用并且可以在 ProgressChanged 事件处理程序中更新进度。

MSDN Reference

Can any one tel me what is wrong in the code below and how to fix it?

问题就在这里

public void testTaskUpdateLabel(string labelTeks)
{
    Task taskUpdateLabel = new Task(() =>
    {
        label4.Text = labelTeks;
    });
    taskUpdateLabel.Start(uiScheduler);
}

您不应使用 TPL 更新 UI。 TPL 任务用​​于执行非 UI 工作,并且 UI 应该只在 UI 线程上更新。您已经将工作转移到线程池线程上(通过 Task.Run),因此您需要解决的唯一问题是如何从工作线程内部更新 UI。有很多方法可以做到这一点——使用 Control.Invoke/BeginInvokeSynchronizationContext 等,但 TPL 的首选方法是传递并使用 IProgress<T> interface. Don't be fooled by the name - the interface is an abstraction of a callback with some data. There is a standard BCL provided implementation - Progress<T> class 并具有以下行为,根据文档

Any handler provided to the constructor or event handlers registered with the ProgressChanged event are invoked through a SynchronizationContext instance captured when the instance is constructed.

即非常适合 UI 更新场景。

综上所述,以下是如何将其应用到您的代码中。我们将使用 IProgress<string> 并将调用 Report 方法并传递我们找到的每个 file/directory 的全名 - 直接替换您的 testTaskUpdateLabel 调用。

private void button1_Click(object sender, EventArgs e)
{
    var progress = new Progress<string>(text => label4.Text = text);
    Task.Run(() =>
    {
        WalkDirectory(new DirectoryInfo(drive), progress);
    });
}

public bool WalkDirectory(DirectoryInfo directory, IProgress<string> progress)
{
    if (directory == null) throw new ArgumentNullException("directory");
    if (progress == null) throw new ArgumentNullException("progress");
    return WalkDirectories(directory, progress);
}

bool WalkDirectories(DirectoryInfo directory, IProgress<string> progress)
{
    // ...
    if (!(continueScan = WalkDirectories(subDirectory, progress)))
    // ...
    if (continueScan)
        progress.Report(directory.FullName);
    // ...
}

bool WalkFilesInDirectory(DirectoryInfo directory, IProgress<string> progress)
{
    // ...
    try
    {
        progress.Report(file.FullName);
    }
    // ...
}

我通过使 walkDirectory、walkDirectories 和 WalkFiles 方法异步来让它工作。因此在我调用 testUpdate 和 testUpdateLabel 方法之前使用 await 关键字。这样,当搜索 运行 时,列表视图会随着搜索结果更新,而不会阻塞 UI 线程。 IE。用户可以在找到他要搜索的文件后取消搜索。