在后台线程上进行 IO 或文件传输是非常糟糕的,因为在文件传输或 IO 操作期间,CPU 没有得到使用,所以你的后台线程除了等待 IO 完成之外什么也没做,而你又浪费了一个线程导致内存浪费。另一个缺点是您的 UI 将变得对文件传输没有响应需要相当长的时间,即使您是在后台线程上执行此操作。
我可以继续写在后台线程上执行 IO 工作的缺点,但您应该索取一份关于在 .Net 中异步执行 IO 的文档。
后台线程用于 CPU 密集型工作,例如涉及 CPU 的繁重计算。
您应该使用异步 IO。使用异步和等待。请阅读异步 IO。
我有一个示例,说明您使用带有进度条和响应式 UI 的纯异步 IO 所做的任何事情。我明天写。
**
以下是异步 io 文件传输代码,假设所有文件大小相同以进行进度更新
下面提到的代码将起作用,即使您在传输文件时移动窗口,应用程序也不会挂起。
**
MainWindow.xaml
<Window x:Class="asyncawait.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="514" Width="674">
<Grid Margin="0,0,0,4">
<ScrollViewer Name="scrv_Log" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Left" Width="397" Margin="33,61,0,67">
<ListBox VirtualizingPanel.VirtualizationMode="Recycling" ItemsSource="{Binding Datas,Mode=OneWay}" Background="White" Foreground="Red"/>
</ScrollViewer>
<TextBlock Height="40" Width="70" FontSize="10" Foreground="Green" Text="{Binding FileCounter,Mode=OneWay}" Margin="540,281,56,158"></TextBlock>
<Button Foreground="Black" IsEnabled="{Binding IsButtonEnabled}" Click="Button_Click" Height="40" Width="40" RenderTransformOrigin="4.65,0.425" Margin="540,210,86,229"></Button>
<ProgressBar Value="{Binding Progressvalue,Mode=OneWay}" Name="prg" Foreground="Green" Height="20" Width="600" Margin="23,453,44.2,7"></ProgressBar>
<Label Content="{Binding ElementName=prg,Path=Value}" ContentStringFormat="{}{0}%" Height="25" Margin="253,0,295.2,22" VerticalContentAlignment="Center" VerticalAlignment="Bottom"/>
<TextBox Background='AliceBlue' HorizontalAlignment="Left" Height="23" Margin="525,109,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="97"/>
</Grid>
</Window>
****这是执行异步 IO 的代码**
public partial class MainWindow: Window,INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
public double Progressvalue
{
get
{
return _Progressvalue;
}
set
{
if(value!=_Progressvalue)
{
_Progressvalue=value;
OnPropertyChanged();
}
}
}
private Double _Progressvalue=0;
private bool _IsEnabled=true;
public Boolean IsButtonEnabled {
get
{
return _IsEnabled;
}
set
{
if(value!=_IsEnabled)
{
_IsEnabled = value;
OnPropertyChanged();
}
}
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
IsButtonEnabled = false;
await AsyncTransferFiles();
IsButtonEnabled = true;
scrv_Log.ScrollToBottom();
}
private String fileCounter;
public String FileCounter
{
get
{ return fileCounter; }
set
{
if (value != fileCounter)
{
fileCounter = value;
OnPropertyChanged();
}
}
}
private ObservableCollection<String> _Datas = new ObservableCollection<string>();
public ObservableCollection<String> Datas
{
get
{
return _Datas;
}
}
private async Task AsyncTransferFiles()
{
var fileNames = Directory.GetFiles("C:\\Data1").ToList();
int totalCount = fileNames.Count;
pr = (double)1 / totalCount;
int counter = 0;
var progress = new Progress<double>();
progress.ProgressChanged += (sender, e) =>
{
Progressvalue = Double.Parse(e.ToString());
};
foreach (var fileName in fileNames)
{
await (CopyFileAsync(fileName, "C:\\GradebookTemp1\\" + fileName.Split('\\')[2], progress, ++counter));
}
}
double pr = 0.0;
public async Task CopyFileAsync(string sourcePath, string destinationPath,IProgress<double> progress ,int fileCounter)
{
using (Stream source = File.Open(sourcePath,FileMode.Open))
{
using (Stream destination = File.Create(destinationPath))
{
await source.CopyToAsync(destination);
progress.Report((int)(pr*fileCounter*100));
FileCounter = fileCounter.ToString();
Datas.Add("Copied File: " + sourcePath);
scrv_Log.ScrollToBottom();
}
}
}
private void EnableButton()
{
IsButtonEnabled = true;
}
private void OnPropertyChanged([CallerMemberName] String propertyName=null)
{
var handler = PropertyChanged;
if(null!=handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}