问题
I have a Task which is always blocked and I have a CancellationToken passed into it which is used to cancel the task. However the Continuation task is never executed which is set to execute on Task's cancellation. The code is:
_tokenSrc = new CancellationTokenSource();
var cnlToken = _tokenSrc.Token;
Task.Run(() =>
// _stream.StartStream() blocks forever
_stream.StartStream(), cnlToken)
.ContinueWith(ant =>
{
_logger.Warn("Stream task cancellation requested, stopping the stream");
_stream.StopStream();
_stream = null;
_logger.Warn("Stream stopped and task cancelled");
}, TaskContinuationOptions.OnlyOnCanceled);
Later somewhere else in the code ...
_tokenSrc.Cancel();
The reason I had to use a Task for _stream.StartStream() is that this call blocks forever (an api on which I have no control, note that _stream refers to a thirdparty Api which streams data from a webservice) so I had to invoke it on another thread.
What is the best way to cancel the task?
回答1:
[UPDATE]
I changed the code to below which fixed the problem:
Task.Run(() =>
{
var innerTask = Task.Run(() => _stream.StartStream(), cToken);
innerTask.Wait(cToken);
}, cToken)
.ContinueWith(ant =>
{
_logger.Warn("Stream task cancellation requested, stopping the stream");
_stream.StopStream();
_stream = null;
_logger.Warn("Stream stopped and task cancelled");
}, TaskContinuationOptions.OnlyOnCanceled);
回答2:
You can use the Register
method on CancellationToken
to register a delegate that will be invoked when cancellation is requested. In the delegate you call the method that unblocks your blocked operation.
Something like:
_tokenSrc = new CancellationTokenSource();
var cnlToken = _tokenSrc.Token;
var task = Task.Factory.StartNew(() =>
{
using(var tokenReg = cnlToken.Register(() =>
{
_logger.Warn("Stream task cancellation requested, stopping the stream");
_stream.StopStream();
_stream = null;
_logger.Warn("Stream stopped and task cancelled");
}))
{
_stream.StartStream(), cnlToken)
}
}, cnlToken);
MSDN "How to: Register Callbacks for Cancellation Requests"
回答3:
How to use a cancellation token is clearly described here: http://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx with a suggested pattern to follow.
I'll report the example in case the page goes down:
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = tokenSource2.Token;
var task = Task.Factory.StartNew(() =>
{
// Were we already canceled?
ct.ThrowIfCancellationRequested();
bool moreToDo = true;
while (moreToDo)
{
// Poll on this property if you have to do
// other cleanup before throwing.
if (ct.IsCancellationRequested)
{
// Clean up here, then...
ct.ThrowIfCancellationRequested();
}
}
}, tokenSource2.Token); // Pass same token to StartNew.
tokenSource2.Cancel();
// Just continue on this thread, or Wait/WaitAll with try-catch:
try
{
task.Wait();
}
catch (AggregateException e)
{
foreach (var v in e.InnerExceptions)
Console.WriteLine(e.Message + " " + v.Message);
}
Console.ReadKey();
}
}
There is an even more in-depth example here: http://msdn.microsoft.com/en-us/library/dd537607(v=vs.110).aspx but the first one should be enough for your scenario.
来源:https://stackoverflow.com/questions/22735533/how-do-i-cancel-a-blocked-task-in-c-sharp-using-a-cancellation-token