Как улучшить индикатор выполнения из рекурсивного метода, избегая проблем с многопоточностью? - PullRequest
0 голосов
/ 19 ноября 2009

Я сделал этот пример кода , который успешно использует BackgroundWorker для продвижения индикатора выполнения в цикле for.

Теперь я пытаюсь адаптировать его для работы со следующим методом рекурсивного копирования файлов, чтобы он показывал мне, как далеко идет копирование, но следующий код выдает ошибку " Этот BackgroundWorker в настоящее время занят и не может запускать несколько задач одновременно . "

Что мне нужно изменить, чтобы этот рекурсивный метод не вызывал проблем с многопоточностью?

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel LastChildFill="True" HorizontalAlignment="Left" VerticalAlignment="Top"
                Margin="10">

        <TextBlock DockPanel.Dock="Top" Text="{Binding PageTitle}" Style="{DynamicResource PageTitleStyle}"/>
        <TextBlock DockPanel.Dock="Top" Text="{Binding PageDescription}" Style="{DynamicResource PageDescriptionStyle}"/>

        <Button x:Name="Button_Start" HorizontalAlignment="Left"  DockPanel.Dock="Top" Content="Start Task" Click="Button_Start_Click" Height="25" Width="200"/>

        <ProgressBar x:Name="ProgressBar"
                     HorizontalAlignment="Left"
                    Margin="0 10 0 0"
                    Height="23"
                     Width="500"
                     Minimum="0"
                     Maximum="100"
                     />
    </DockPanel>
</Window>

код-за:

using System.Windows;
using System.ComponentModel;
using System.Threading;
using System.IO;

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        private BackgroundWorker backgroundWorker;
        int thread1percentageFinished = 0;

        private int totalFilesToCopy;
        private int numberOfFilesCopied;

        public MainWindow()
        {
            InitializeComponent();

            ProgressBar.Visibility = Visibility.Collapsed;
        }

        private void Button_Start_Click(object sender, RoutedEventArgs e)
        {
            int totalFilesToCopy = 1000;
            int numberOfFilesCopied = 0;

            backgroundWorker = new BackgroundWorker();
            backgroundWorker.WorkerReportsProgress = true;
            backgroundWorker.WorkerSupportsCancellation = true;
            ProgressBar.Visibility = Visibility.Visible;

            CopyFolder(@"C:\test", @"C:\test2");
        }


        void CopyFolder(string sourceFolder, string destFolder)
        {
            backgroundWorker.DoWork += (s, args) =>
            {
                BackgroundWorker worker = s as BackgroundWorker;
                if (!Directory.Exists(destFolder))
                    Directory.CreateDirectory(destFolder);
                string[] files = Directory.GetFiles(sourceFolder);
                foreach (string file in files)
                {
                    string name = Path.GetFileName(file);
                    string dest = Path.Combine(destFolder, name);
                    File.Copy(file, dest, true);
                    numberOfFilesCopied++;
                    float percentageDone = (numberOfFilesCopied / (float)totalFilesToCopy) * 100f;
                    worker.ReportProgress((int)percentageDone);
                }
                string[] folders = Directory.GetDirectories(sourceFolder);
                foreach (string folder in folders)
                {
                    string name = Path.GetFileName(folder);
                    string dest = Path.Combine(destFolder, name);
                    CopyFolder(folder, dest);
                }
            };

            backgroundWorker.ProgressChanged += (s, args) =>
            {
                thread1percentageFinished = args.ProgressPercentage;
                ProgressBar.Value = thread1percentageFinished;
            };


            backgroundWorker.RunWorkerCompleted += (s, args) =>
            {
                Button_Start.IsEnabled = true;
                ProgressBar.Visibility = Visibility.Collapsed;
                ProgressBar.Value = 0;
            };

            backgroundWorker.RunWorkerAsync();
        }



    }
}

Ответы [ 2 ]

4 голосов
/ 19 ноября 2009

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

То, как вы это делаете, индикатор выполнения будет перескакивать повсюду, и вы будете предоставлять ненадежную обратную связь пользователю. Они могут дойти до самого «конца» копии, а затем внезапно превратиться в очень большую структуру папок, отодвинув полосу назад к началу.

Если вы хотите продолжать делать то же, что и вы, вам нужно перенести всю подготовку BackgroundWorker к вашему Button_Start_Click методу, а также вызов RunWorkerAsync(). Вы сохраняете все «кишки» существующего определения CopyFolder DoWork в самом CopyFolder, но вместо этого передаете ссылку на фонового работника:

backgroundWorker.DoWork += (s, args) => 
{
    CopyFolder(@"C:\test", @"C:\test2", s);
};

Тогда вы можете получить CopyFolder, который будет выглядеть примерно так:

void CopyFolder(string sourceFolder, string destFolder, BackgroundWorker worker)
{
    if (!Directory.Exists(destFolder))
        ... // the rest is unchanged
}

Таким образом, вы только создаете и запускаете один BackgroundWorker и просто передаете ссылку на него по дереву вызовов.

1 голос
/ 19 ноября 2009

Проблема в том, что вы вызываете одного и того же фонового работника для каждой итерации обхода каталога. Все, что вам нужно, это один фоновый работник, который на самом деле делает работу. Весь рекурсивный обход должен содержаться в одном вызове DoWork (вместо каждого сканирования подкаталогов). Тогда у вас есть основной поток пользовательского интерфейса, обновляющий ваш индикатор выполнения.

...