2009-11-19 304 views
0

我做了this code example,它成功地使用了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(); 
     } 



    } 
} 

回答

1

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

4

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

你这样做的方式,进度条会跳到所有的地方,你会提供不可靠的反馈给用户。他们可能会到达一个副本的“末端”,然后突然缓慢下移到一个非常大的文件夹结构中,从而一直推回到开始。

如果您想继续按照您的方式进行操作,则需要将BackgroundWorker的所有准备工作移至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和刚好路过一个参考下来调用树。