问题
I have a Task which retrieves some data from a Web Service. The Webservice should only get called when I open a page (this is Xamarin.Forms) and it should only run if there isn't another version of the task running - This part works fine.
When I move away from the page, I want to cancel the Task - I can't seem to get that piece working. When OnAppearing()
, OnDisappearing()
, OnAppearing() is hit before the webservice returns the data, logs just write "Task has attempted to start..." which means that the Task didn't get cancelled. Instead the task should have been cancelled when
OnDisappearing()` was called.
I have tried to follow some of the examples posted on StackOverflow but doesn't seem to work and I can't quite figure it out.
The code I have is below:
OnAppearing:
private Task task;
CancellationTokenSource tokenSource = new CancellationTokenSource();
protected override void OnAppearing()
{
base.OnAppearing();
if (task != null && (task.Status == TaskStatus.Running || task.Status == TaskStatus.WaitingToRun || task.Status == TaskStatus.WaitingForActivation))
{
Debug.WriteLine("Task has attempted to start while already running");
}
else
{
Debug.WriteLine("Task has began");
var token = tokenSource.Token;
task = Task.Run(async () =>
{
await GetDataAsync();
/* EDIT: This is what I originally posted, but doesn't work so have commented it out
while (!token.IsCancellationRequested)
{
await GetDataAsync();
}
*/
}, token);
}
}
OnDisappearing:
protected override void OnDisappearing()
{
base.OnDisappearing();
Debug.WriteLine("Page dissapear");
tokenSource.Cancel();
Debug.WriteLine("Task Cancelled");
}
GetData Method
public async Task GetData()
{
WebAPI api = new WebAPI();
try
{
Device.BeginInvokeOnMainThread(() =>
{
PageLoading();
});
string r = await api.GetProfileStatus(token);
Device.BeginInvokeOnMainThread(async () =>
{
if (r == "OK")
{
GetDataSuccess();
}
}
}
catch (Exception e)
{
// log exception
}
}
回答1:
You're doing the right check in a wrong place:
var token = tokenSource.Token;
task = Task.Run(async () =>
{
while (!token.IsCancellationRequested)
{
await GetDataAsync();
}
}
Here you start the task, and after that start it again and again in a loop. The right thing to do is check the token inside your GetData
, and futher to api
variable, if possible. In this case you'll be able to cancel the service call as you need.
Side notes:
- make your event handlers
async
, and do not wrap your work inTask.Run
- make your
GetData
Task<T>
so you can actually return status to the calling code, then you can removeBeginInvokeOnMainThread
, asawait
will restore the context after async operation
So your code will be something like this:
protected override async void OnAppearing()
{
base.OnAppearing();
if (task != null && (task.Status == TaskStatus.Running || task.Status == TaskStatus.WaitingToRun || task.Status == TaskStatus.WaitingForActivation))
{
Debug.WriteLine("Task has attempted to start while already running");
}
else
{
Debug.WriteLine("Task has began");
var token = tokenSource.Token;
PageLoading();
var r = await GetDataAsync(token);
if (r == "OK")
{
GetDataSuccess();
}
}
}
public async Task GetData(CancellationToken token)
{
WebAPI api = new WebAPI();
if (token.IsCancellationRequested)
{
token.ThrowIfCancellationRequested();
}
try
{
return await api.GetProfileStatus(token);
}
catch (Exception e)
{
// log exception and return error
return "Error";
}
}
来源:https://stackoverflow.com/questions/49499495/how-to-cancel-task-c-sharp