如何通过递归方法推进进度条,同时避免线程问题?
我制作了此代码示例,它成功地使用了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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
实际上,最好使用递归来查找需要完成的所有工作,然后设置 BackgroundWorker,然后在非递归循环中启动工作,根据需要更新进度,然后关闭
BackgroundWorker
。按照您的方式,进度条会到处乱跳,并且您将向用户提供不可靠的反馈。他们可能会到达副本的“末尾”,然后突然递归到一个非常大的文件夹结构,将栏一直推回到开头。
但是,如果您想继续按照现在的方式进行操作,则需要将
BackgroundWorker
的所有准备工作以及Button_Start_Click
方法移至Button_Start_Click
方法中。 >RunWorkerAsync() 调用。您可以在 CopyFolder 本身中保留现有CopyFolder
的DoWork
定义的所有“内容”,而是传入对后台工作人员的引用:然后您可以拥有一个
CopyFolder
看起来更像是这样:这样您只需创建并启动一个
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 theBackgroundWorker
.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 yourButton_Start_Click
method, as well as theRunWorkerAsync()
call. You retain all of the "guts" of your existingCopyFolder
'sDoWork
definition in CopyFolder itself, but instead pass in a reference to the background worker:Then you can have a
CopyFolder
that looks more like this:This way you're only creating and starting up a single
BackgroundWorker
and just passing a reference to it down the call tree.问题是您为目录遍历的每次迭代调用相同的后台工作程序。您所需要的只是一名实际完成工作的后台工作人员。整个递归遍历应包含在对 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.