Related to this question: I need to cancel long running loop in signalR, how do i do this?
I set up a quick way to better understand the situation for myself and found Half a solution. I'm able to cancel a task (not sure how since the CancellationTokenSource is null at .Cancel() but whatever). But there seems to be a new problem. This is my current test code:
The server side hub:
public class MyHub : Hub<IMyHub>
{
private CancellationTokenSource cancellationTokenSource;
public void StartLongRunningTask()
{
cancellationTokenSource = new CancellationTokenSource();
try
{
LongRunningTask(cancellationTokenSource.Token);
// Task completed successfully
}
catch (TaskCanceledException)
{
// Task was canceled by the client
// Handle cancellation logic here
}
finally
{
cancellationTokenSource.Dispose();
}
}
public async void CancelLongRunningTask()
{
cancellationTokenSource?.Cancel();
await Clients.All.SendStatus(false);
}
private void LongRunningTask(CancellationToken ct)
{
// Your long-running task implementation
// Ensure to check the cancellation token periodically and stop processing if canceled
while (!ct.IsCancellationRequested)
{
_ = Clients.Caller.SendStatus(true);
_ = Task.Delay(1000, ct);
// Continue with other work...
}
}
}
The client side caller:
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly HubConnection _hubConnection;
public bool Running { get; set; }
public string Status { get; set; }
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
_hubConnection = new HubConnectionBuilder()
.WithUrl("https://localhost:7127/myhub") // Replace with the actual URL of your SignalR hub
.Build();
_ = _hubConnection.StartAsync();
_hubConnection.On<bool>("SendStatus", (status) =>
{
Running = status;
});
}
public async Task OnGetAsync()
{
// Start the SignalR connection
}
public async Task<IActionResult> OnPostOpenAsync(IFormCollection data)
{
Status = _hubConnection.State.ToString();
return Page();
}
public async Task<IActionResult> OnPostStartAsync(IFormCollection data)
{
_ = _hubConnection.InvokeAsync("StartLongRunningTask");
Status = _hubConnection.State.ToString();
return Page();
}
public async Task<IActionResult> OnPostCancelAsync(IFormCollection data)
{
await _hubConnection.InvokeAsync("CancelLongRunningTask");
Status = _hubConnection.State.ToString();
return Page();
}
}
And you could guess there are buttons behind the open, start and cancel function. But a couple of interesting things happen here.
If I await the invocation of LongRunningTask it will wait untill it is finished, but it never is until I cancel it. So far it's logical to me.
But when I don't await LongRunningTask(CancellationTokenSource.Token); in the Server hub. It should not wait for this task to finish right? Or am I missing something here?
I'm not sure why but Clients.Caller.SendStatus(true) won't send anything untill I move it to the StartlongRunningTask() in the server hub. I could think of a reason why this is but i'm not sure and I would like to be so if someone can explain this behaviour to me, that would be great.
Last but not least, the core reason I made this post. If i await the invocation on the clientside it will wait before I clicked cancel. But when I do this is the result in memory and cpu usage:
The first blue line is where I start the long running task and the second is where I cancel the task. I believe somehow the task keeps running in the background but the frontend stops loading as soon as i click and the "CancelLongRunningTask" on the server hub is triggered. So how does this happen really? The frontend stops loading but in the background it keeps the loop? If anyone could explain this behaviour to me this would be great!
edited
After awaiting everything as Canton7 suggested there is still a question to be answered since the CancelLongRunningTask() function calls Cancel on an empty token? Apart from that, the .SendStatus(true) is never send?