Wpf 任务取消:调用ThrowIfCancellationRequested()时,while循环未正确退出
我开发了一个小项目(使用MVVM),它具有将文件上载到FTP服务器的功能 用户可以查看上传进度:完成百分比通过vm.Busycontent显示给用户,vm.Busycontent是我的视图模型中绑定到我视图中的UI元素的属性 下面是读取文件并通过FTP上传的代码(这是任务vm.FtpUploadTask的一部分) main window.xaml 我正在使用WPF扩展工具包BusyIndicatorWpf 任务取消:调用ThrowIfCancellationRequested()时,while循环未正确退出,wpf,while-loop,task-parallel-library,task,cancellation,Wpf,While Loop,Task Parallel Library,Task,Cancellation,我开发了一个小项目(使用MVVM),它具有将文件上载到FTP服务器的功能 用户可以查看上传进度:完成百分比通过vm.Busycontent显示给用户,vm.Busycontent是我的视图模型中绑定到我视图中的UI元素的属性 下面是读取文件并通过FTP上传的代码(这是任务vm.FtpUploadTask的一部分) main window.xaml 我正在使用WPF扩展工具包BusyIndicator <xctk:BusyIndicator IsBusy='{Binding IsBusy}'
<xctk:BusyIndicator IsBusy='{Binding IsBusy}'>
<xctk:BusyIndicator.BusyContentTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text='{Binding Path=DataContext.BusyContent,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}' />
</StackPanel>
</DataTemplate>
</xctk:BusyIndicator.BusyContentTemplate>
</xctk:BusyIndicator>
CancelCommand.cs
try
{
vm.FtpUploadTask = new Task(() => FTPUpload(file), vm.Token);
vm.FtpUploadTask.Start();
vm.FtpUploadTask.Wait(vm.Token);
vm.BusyContent = "upload done!";
}
catch (OperationCanceledException)
{
vm.BusyContent = "Canceled";
}
public class CancelCommand : ICommand
{
public void Execute(object parameter)
{
vm.TokenSource.Cancel();
}
}
cancel函数起作用,但有时vm.Busycontent等于
- ((int)progress.ToString()
- 已在上载FTPCommand中取消
- 我正在使用.NET4.0
- 该程序是一个较大项目的一部分,该项目包括应以同步方式执行的多个任务。这就是我使用Task.Start()和Task.Wait()方法的原因
using (FileStream inputStream = File.OpenRead(file))
{
using (outputStream = request.GetRequestStream())
{
var buffer = new byte[1024 * 1024];
int totalReadBytesCount = 0;
int readBytesCount;
while ((readBytesCount = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
if (vm.Token.IsCancellationRequested)
{
break;
}
outputStream.Write(buffer, 0, readBytesCount);
totalReadBytesCount += readBytesCount;
var progress = totalReadBytesCount * 100.0 / inputStream.Length;
vm.BusyContent = ((int)progress).ToString();
}
if (vm.Token.IsCancellationRequested)
{
inputStream.Close();
outputStream.Close();
vm.Token.ThrowIfCancellationRequested();
}
}
}您可能会遇到一个与您如何设置代码有关的问题。如果不仔细检查每一行,就很难判断。但更重要的是: 按下取消按钮时,while循环应退出,用户应仅在catch中看到消息(OperationCanceledException) 这正好显示了如何退出
while
循环并抛出一个可以捕获的异常。注意,测试断言TaskCanceledException
,但您刚好捕获了OperationCanceledException
using System.Threading.Tasks;
using System.IO;
using System.Threading;
using NUnit.Framework;
namespace CancellationTests {
[TestFixture]
public class WhileCancellation {
[Test]
public void While_Loop_Is_Canceled_When_Cancel_Is_Requested() {
var inputFile = new FileInfo("somebigfile.txt");
var outputFile = new FileInfo("outputFile.txt");
var cts = new CancellationTokenSource();
cts.Cancel();
Assert.ThrowsAsync<TaskCanceledException>(() => TheWhileLoop(inputFile, outputFile, cts.Token));
}
private async Task TheWhileLoop(FileInfo inputFile, FileInfo outputFile, CancellationToken token) {
using (var inputStream = inputFile.OpenRead())
using (var outputStream = outputFile.OpenWrite()) {
var buffer = new byte[1024 * 1024];
var totalReadBytesCount = 0;
var readBytesCount = 0;
while ((readBytesCount = await inputStream.ReadAsync(buffer, 0, buffer.Length, token)) > 0) {
await outputStream.WriteAsync(buffer, 0, readBytesCount, token);
token.ThrowIfCancellationRequested();
totalReadBytesCount += readBytesCount;
var progress = totalReadBytesCount * 100.0 / inputStream.Length;
vm.BusyContent = ((int)progress).ToString();
}
}
}
private static class vm {
public static string BusyContent { get; set; }
}
}
}
使用System.Threading.Tasks;
使用System.IO;
使用系统线程;
使用NUnit.Framework;
命名空间取消测试{
[测试夹具]
取消时的公共类{
[测试]
请求取消时取消循环时公共无效(){
var inputFile=newfileinfo(“somebigfile.txt”);
var outputFile=newfileinfo(“outputFile.txt”);
var cts=新的CancellationTokenSource();
cts.Cancel();
Assert.ThrowsAsync(()=>whileloop(inputFile、outputFile、cts.Token));
}
私有异步任务whileloop(FileInfo输入文件、FileInfo输出文件、CancellationToken令牌){
使用(var inputStream=inputFile.OpenRead())
使用(var outputStream=outputFile.OpenWrite()){
var buffer=新字节[1024*1024];
var totalReadBytesCount=0;
var readbytescont=0;
而((readBytesCount=await inputStream.ReadAsync(缓冲区,0,缓冲区,长度,标记))>0){
等待outputStream.WriteAsync(缓冲区,0,readBytesCount,令牌);
token.ThrowIfCancellationRequested();
totalReadBytesCount+=readBytesCount;
var progress=totalReadBytesCount*100.0/inputStream.Length;
vm.BusyContent=((int)progress.ToString();
}
}
}
私有静态类vm{
公共静态字符串BusyContent{get;set;}
}
}
}
希望这能让您走上正轨。您可能会遇到一个与您如何设置代码有关的问题。如果不仔细检查每一行,就很难判断。但更重要的是: 按下取消按钮时,while循环应退出,用户应仅在catch中看到消息(OperationCanceledException) 这正好显示了如何退出
while
循环并抛出一个可以捕获的异常。注意,测试断言TaskCanceledException
,但您刚好捕获了OperationCanceledException
using System.Threading.Tasks;
using System.IO;
using System.Threading;
using NUnit.Framework;
namespace CancellationTests {
[TestFixture]
public class WhileCancellation {
[Test]
public void While_Loop_Is_Canceled_When_Cancel_Is_Requested() {
var inputFile = new FileInfo("somebigfile.txt");
var outputFile = new FileInfo("outputFile.txt");
var cts = new CancellationTokenSource();
cts.Cancel();
Assert.ThrowsAsync<TaskCanceledException>(() => TheWhileLoop(inputFile, outputFile, cts.Token));
}
private async Task TheWhileLoop(FileInfo inputFile, FileInfo outputFile, CancellationToken token) {
using (var inputStream = inputFile.OpenRead())
using (var outputStream = outputFile.OpenWrite()) {
var buffer = new byte[1024 * 1024];
var totalReadBytesCount = 0;
var readBytesCount = 0;
while ((readBytesCount = await inputStream.ReadAsync(buffer, 0, buffer.Length, token)) > 0) {
await outputStream.WriteAsync(buffer, 0, readBytesCount, token);
token.ThrowIfCancellationRequested();
totalReadBytesCount += readBytesCount;
var progress = totalReadBytesCount * 100.0 / inputStream.Length;
vm.BusyContent = ((int)progress).ToString();
}
}
}
private static class vm {
public static string BusyContent { get; set; }
}
}
}
使用System.Threading.Tasks;
使用System.IO;
使用系统线程;
使用NUnit.Framework;
命名空间取消测试{
[测试夹具]
取消时的公共类{
[测试]
请求取消时取消循环时公共无效(){
var inputFile=newfileinfo(“somebigfile.txt”);
var outputFile=newfileinfo(“outputFile.txt”);
var cts=新的CancellationTokenSource();
cts.Cancel();
Assert.ThrowsAsync(()=>whileloop(inputFile、outputFile、cts.Token));
}
私有异步任务whileloop(FileInfo输入文件、FileInfo输出文件、CancellationToken令牌){
使用(var inputStream=inputFile.OpenRead())
使用(var outputStream=outputFile.OpenWrite()){
var buffer=新字节[1024*1024];
var totalReadBytesCount=0;
var readbytescont=0;
而((readBytesCount=await inputStream.ReadAsync(缓冲区,0,缓冲区,长度,标记))>0){
等待outputStream.WriteAsync(缓冲区,0,readBytesCount,令牌);
token.ThrowIfCancellationRequested();
totalReadBytesCount+=readBytesCount;
var progress=totalReadBytesCount*100.0/inputStream.Length;
vm.BusyContent=((int)progress.ToString();
}
}
}
私有静态类vm{
公共静态字符串BusyContent{get;set;}
}
}
}
希望这能让你走上正轨。你正在检查
令牌.IsCancellationRequested
并在执行throwifcCancellationRequested()
之前退出循环。这是很多代码,你能发布一个吗?@svick我编辑我的问题以减少数量code@JSteward我已经编辑了代码,但问题仍然存在。有什么想法吗?你在检查