wpf - Task cancellation: while loop not exited properly when ThrowIfCancellationRequested() is called -
i've developed small project (using mvvm) functionality upload file ftp-server.
the user can view uploading progress: percentage completed shown user vm.busycontent, property in viewmodel bound ui element in view.
here code reading file , uploading via ftp (which part of task vm.ftpuploadtask)
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 && (!vm.token.iscancellationrequested)) { vm.token.throwifcancellationrequested(); outputstream.write(buffer, 0, readbytescount); totalreadbytescount += readbytescount; var progress = totalreadbytescount * 100.0 / inputstream.length; vm.busycontent = ((int)progress).tostring(); } } }
mainwindow.xaml
i using wpf extended toolkit busyindicator
<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>
uploadtoftpcommand.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"; }
cancelcommand.cs
public class cancelcommand : icommand { public void execute(object parameter) { vm.tokensource.cancel(); } }
the cancel function works vm.busycontent equals
- ((int)progress).tostring()
- canceled in uploadftpcommand
when pressing cancel button, while loop should exited , user should see message in catch (operationcanceledexception). ideas how solve this?
notes
- i using .net 4.0
- this program part of larger project, includes multipe tasks should executed in synchronous manner. that's why using task.start() , task.wait() methods.
edit problem still remains
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(); } }
}
you may have issue relating how you've set code. without going through every line of it, it's difficult tell. more point:
when pressing cancel button, while loop should exited , user should see message in catch (operationcanceledexception).
this shows how exit while
loop , throw exception can catch. note test asserts taskcanceledexception
right in catching 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; } } } }
hopefully gets on right track.
Comments
Post a Comment