如何通过递归方法推进进度条,同时避免线程问题?

发布于 2024-08-11 17:29:50 字数 4216 浏览 2 评论 0原文

我制作了此代码示例,它成功地使用了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();
        }



    }
}

I made this code example which successfully uses a BackgroundWorker to advance a progress bar in a for loop.

Now I'm trying to adapt it to work with the following recursive file copy method so that it shows me how far along the copying is, but the following code is giving me the error "This BackgroundWorker is currently busy and cannot run multiple tasks concurrently."

What do I need to change so that this recursive method does not get these threading issues?

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>

code-behind:

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();
        }



    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

浅唱ヾ落雨殇 2024-08-18 17:29:50

实际上,最好使用递归来查找需要完成的所有工作,然后设置 BackgroundWorker,然后在非递归循环中启动工作,根据需要更新进度,然后关闭BackgroundWorker

按照您的方式,进度条会到处乱跳,并且您将向用户提供不可靠的反馈。他们可能会到达副本的“末尾”,然后突然递归到一个非常大的文件夹结构,将栏一直推回到开头。

但是,如果您想继续按照现在的方式进行操作,则需要将 BackgroundWorker 的所有准备工作以及 Button_Start_Click 方法移至 Button_Start_Click 方法中。 >RunWorkerAsync() 调用。您可以在 CopyFolder 本身中保留现有 CopyFolderDoWork 定义的所有“内容”,而是传入对后台工作人员的引用:

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 并仅在调用树中传递对它的引用。

It would actually be better to use recursion to find all of the work you need done, then set up the BackgroundWorker, then start the work in a non-recursive loop, updating progress as needed, then close out the BackgroundWorker.

The way you're doing it, the progress bar will be jumping all over the place and you'll be providing unreliable feedback to the user. They might get to the very "end" of a copy and then suddenly recurse down into a very large folder structure, pushing the bar all the way back to the beginning.

If you want to keep doing it the way you are, though, you need to move all of the preparation of the BackgroundWorker to your Button_Start_Click method, as well as the RunWorkerAsync() call. You retain all of the "guts" of your existing CopyFolder's DoWork definition in CopyFolder itself, but instead pass in a reference to the background worker:

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

Then you can have a CopyFolder that looks more like this:

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

This way you're only creating and starting up a single BackgroundWorker and just passing a reference to it down the call tree.

混吃等死 2024-08-18 17:29:50

问题是您为目录遍历的每次迭代调用相同的后台工作程序。您所需要的只是一名实际完成工作的后台工作人员。整个递归遍历应包含在对 DoWork 的单个调用中(而不是每次子目录扫描)。然后你只需让主 UI 线程更新你的进度条。

The problem is that you are invoking the same background worker for each iteration of the directory walk. All you need is one background worker that actually does the work. The entire recursive walk should be contained within a single call to DoWork (instead of each sub-directory scan). Then you just have the main UI thread updating your progress bar.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文