0

I can't find a way to cancel an async call running on the background thread. Let's say I have something like this:

private async void myMethod()
{
    String result = await Task.Run(async () => await myTimeConsumingMethod());
    //Do stuff with my result string...
}

Here I'm calling an async method on my background thread, and since I don't have any loops I can't do something like this:

for (i = 0; i < someBigNumber; i++)
{
    token.ThrowIfCancellationRequested();
    await doSomeWork();
}

Instead I have a single call to an async method that can take more than 10 seconds to complete, I can't check if the token has been canceled inside that method, since I'm awaiting for the async call to complete.

This is what I'm trying to achieve:

private async void myMethod()
{
    String result = await Task.Run(async () => await myTimeConsumingMethod());

    //Override the HardwareButtons.BackPressed event (I already have a method for that)

    //When the await is completed, the String is the result of the async 
    //call if the method has completed, otherwise the result String 
    //should be set to null.
}

The problem is that I don't know what code to use inside the HardwareButtons.BackPressed event to terminate my async call. I mean, I literally want to "terminate its process" and make that Task.Run instantly return null.

Is there a way to do that?

This is the implementation of myTimeConsumingMethod():

public static async Task<String> ToBase64(this StorageFile bitmap)
{
    IRandomAccessStream imageStream = await CompressImageAsync(bitmap);
    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);
    PixelDataProvider pixels = await decoder.GetPixelDataAsync();
    byte[] bytes = pixels.DetachPixelData();
    return await ToBase64(bytes, (uint)decoder.PixelWidth, (uint)decoder.PixelHeight, decoder.DpiX, decoder.DpiY);
}
Servy
  • 202,030
  • 26
  • 332
  • 449
Sergio0694
  • 4,447
  • 3
  • 31
  • 58

1 Answers1

1

This is impossible. You can create a Task that will complete when that existing operation completes, or mark itself as cancelled if a cancellation token is cancelled, but that won't actually stop the original operation, merely allow the program to continue executing despite the fact that the operation isn't actually done yet.

See this blog article for more information on the subject.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • Ok, so I could do something like this: create the Task, declare a String that awaits that Task and inside the BackPressed event cancel that Task, right? If I cancel that Task so the method will continue even if it hasn't finished yet? My String will be null at that point, since the Task hasn't returned a value, right? – Sergio0694 Feb 16 '15 at 20:36
  • @Sergio0694 You can create a method that behaves identically to another `Task` (or `Task`) but that will mark itself as cancelled if a `CancellationToken` given to it is marked as cancelled before the underlying `Task` has finished. This would have no effect on the original task, nor would it stop the operation being performed. Creating such a method is a [one liner](http://stackoverflow.com/a/25652873/1159478). – Servy Feb 16 '15 at 20:40
  • I used that suggestion you posted and I came up with a solution, I updated my post with it. Could you tell me what do you think and if it's fine that way? Thank you again! :) – Sergio0694 Feb 16 '15 at 21:14
  • @Sergio0694 If it's working for you, then you can go ahead and use it. I can't know if it actually does what you want it to do. – Servy Feb 16 '15 at 21:18
  • Yup, it throws an Exception so my await call stops and I can exit the method. My only doubt is whether or not my async method is still running in the background even after the async call throws that exception. Because if that's the case, I should find a way to stop that to avoid wasting resources. – Sergio0694 Feb 16 '15 at 21:22
  • @Sergio0694 As I've said, all this does is create a new task that will mark itself as cancelled when the token cancels itself; it will do nothing to cancel the underlying operation. – Servy Feb 16 '15 at 21:25
  • Ok, I'll see if I can figure something out there then :) Sorry for my many questions, I've never used the Task classes other than for simple await calls, thanks for your help! – Sergio0694 Feb 16 '15 at 21:37
  • This is why I prefer using Threads. Instead of using async/await, just create a new thread and run the method - if it takes too long, you can simply abort it. new Thread(() => {myTimeConsumingMethod();}).Start(); – user1274820 Feb 16 '15 at 21:53
  • @user1274820 Nevermind the *vast* array of very serious and yet extraordinarily hard to debug problems that are likely to arise as a result. I mean, at least it appears to work at first glance, even if it's extremely fragile. There's a reason that `Task` doesn't support aborting the thread, and it's because it's not something that's safe to do. – Servy Feb 16 '15 at 22:00
  • 2
    I don't like hearing that something as simple as cancelling an operation is "Impossible" and seeing it marked as answer. It's not "impossible." You can complain about implementations, but that's what error handling is for. Force cancel the operation and report any relevant exceptions - it is not impossible. – user1274820 Feb 16 '15 at 22:02
  • @user1274820 If it's not impossible then go ahead and post an answer that shows how to cancel an arbitrary `Task` that wasn't built to support cancellation. Note that it's quite possible that there is no actual thread performing the work that could be aborted; the`Task` can be, for example, representing async IO, and so there is no thread. – Servy Feb 16 '15 at 22:06
  • @user1274820 I know it's not impossible to do, you can even do it in plain C by killing a certain process by its pid. I marked the answer as valid because from what I saw on MSDN, WinRT doesn't implement the Thread class, so you are forced to use Tasks. And as Servy pointed out, there isn't a way to directly kill a running Task. – Sergio0694 Feb 16 '15 at 22:07