[英]Async/await with CancellationToken doesn't cancel the operation
I want to use the CancellationToken
to abort a file download. 我想使用CancellationToken
中止文件下载。 This is what I tried: 这是我尝试的:
public async Task retrieveDocument(Document document)
{
// do some preparation work first before retrieving the document (not shown here)
if (cancelToken == null)
{
cancelToken = new CancellationTokenSource();
try
{
Document documentResult = await webservice.GetDocumentAsync(document.Id, cancelToken.Token);
// do some other stuff (checks ...)
}
catch (OperationCanceledException)
{
Console.WriteLine("abort download");
}
finally
{
cancelToken = null;
}
}
else
{
cancelToken.Cancel();
cancelToken = null;
}
}
public async Task<Document> GetDocumentAsync(string documentId, CancellationToken cancelToken)
{
Document documentResult = new Document();
try
{
cancelToken.ThrowIfCancellationRequested();
documentResult = await Task.Run(() => manager.GetDocumentById(documentId));
}
return documentResult;
}
The cancelToken should then be used to cancel the operation: cancelToken然后应用于取消操作:
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
if (cancelToken != null) {
Console.WriteLine ("Token cancelled");
cancelToken.Cancel ();
}
}
It seems that IsCancellationRequested
is not updated. 似乎IsCancellationRequested
没有更新。 So the operation is not cancelled. 因此操作不会被取消。 I also tried to use this 我也试图用这个
cancelToken.ThrowIfCancellationRequested();
try{
documentResult = await Task.Run(() => manager.GetDocumentById (documentId), cancelToken);
} catch(TaskCanceledException){
Console.WriteLine("task canceled here");
}
but nothing changed. 但是什么都没有改变。
What I'm doing wrong? 我做错了什么?
Edit: 编辑:
Here are the missing parts like GetDocumentById
: 以下是缺少的部分,例如GetDocumentById
:
public Document GetDocumentById(string docid)
{
GetDocumentByIdResult res;
try
{
res = ws.CallGetDocumentById(session, docid);
}
catch (WebException e)
{
throw new NoResponseFromServerException(e.Message);
}
return res;
}
public Document CallGetDocumentById(Session session, string parmsstring)
{
XmlDocument soapEnvelope = Factory.GetGetDocumentById(parmsstring);
HttpWebRequest webRequest = CreateWebRequest(session);
webRequest = InsertEnvelope(soapEnvelope, webRequest);
string result = WsGetResponseString(webRequest);
return ParseDocument(result);
}
static string WsGetResponseString(WebRequest webreq)
{
string soapResult = "";
IAsyncResult asyncResult = webreq.BeginGetResponse(null, null);
if (asyncResult.AsyncWaitHandle.WaitOne(50000))
{
using (WebResponse webResponse = webreq.EndGetResponse(asyncResult))
{
if (webResponse != null)
{
using (var rd = new StreamReader(webResponse.GetResponseStream()))
{
soapResult = rd.ReadToEnd();
}
}
}
}
else
{
webreq.Abort();
throw new NoResponseFromServerException();
}
return soapResult;
}
I want to use the CancellationToken to abort a file download 我想使用CancellationToken中止文件下载
Downloading a file is an I/O operation, for which asynchronous cancelable (I/O completion port based) functions are available on the .NET platform. 下载文件是一项I / O操作,.NET平台上提供了异步可取消(基于I / O完成端口)功能。 Yet you seem to not be using them. 但是您似乎并没有使用它们。
Instead you appear to be creating (a chain of) tasks using Task.Run
that perform blocking I/O, where a cancelation token is not passed on to each task in your Task.Run
chain. 相反,您似乎正在使用执行阻塞I / O的Task.Run
创建(一系列)任务,其中取消令牌没有传递到Task.Run
链中的每个任务。
For examples of doing async, awaitable and cancelable file downloads, refer to: 有关执行异步,等待和可取消文件下载的示例,请参考:
HttpClient
: How to copy HttpContent async and cancelable? 使用HttpClient
: 如何异步复制和取消HttpContent? WebClient
: Has its own cancellation mechanism: the CancelAsync method, you can connect it to your cancellation token, using the token's Register method: 使用WebClient
:有其自己的取消机制: CancelAsync方法,您可以使用令牌的Register方法将其连接到取消令牌: myToken.Register(myWebclient.CancelAsync); myToken.Register(myWebclient.CancelAsync);
You can do the following: 您可以执行以下操作:
static async Task<string> WsGetResponseString(WebRequest webreq, CancellationToken cancelToken)`
{
cancelToken.Register(webreq.Abort);
using (var response = await webReq.GetResponseAsync())
using (var stream = response.GetResponseStream())
using (var destStream = new MemoryStream())
{
await stream.CopyToAsync(destStream, 4096, cancelToken);
return Encoding.UTF8.GetString(destStream.ToArray());
}
}
Your code only calls ThrowIfCancellationRequested()
once after starting the GetDocumentAsync method, making the window for catching a cancel very small. 您的代码在启动GetDocumentAsync方法后仅调用ThrowIfCancellationRequested()
一次,这使得捕获取消的窗口非常小。
You need to pass the CancellationToken
to GetDocumentById and have it either call ThrowIfCancellationRequested
in between operations or perhaps pass the token straight to some calls at a lower level. 您需要将CancellationToken
传递给GetDocumentById,并ThrowIfCancellationRequested
在操作之间调用ThrowIfCancellationRequested
,或者将令牌直接传递给较低级别的某些调用。
As a quick test of the plumbing between your calling method and the CancellationToken
, you could change GetDocumentAsync
to read: 为了快速测试调用方法和CancellationToken
之间的关系,您可以将GetDocumentAsync
更改为:
cancelToken.ThrowIfCancellationRequested();
documentResult = await Task.Run(() => manager.GetDocumentById(documentId));
cancelToken.ThrowIfCancellationRequested();
And call CancelToken.CancelAfter(50)
or simlar just after creating the CancellationTokenSource
... You may need to adjust the value of 50 depending on how long GetDocumentById
takes to run. 并在创建CancellationTokenSource
之后立即调用CancelToken.CancelAfter(50)
或类似方法...您可能需要根据GetDocumentById
运行多长时间来调整值50。
[Edit] [编辑]
Given your edit to the question, the quickest fix is to pass the CancelToken
down to WsGetResponseString
and use CancelToken.Register()
to call WebRequest.Abort()
. 根据您对问题的编辑,最快的解决方法是将CancelToken
向下传递给WsGetResponseString
并使用CancelToken.Register()
调用WebRequest.Abort()
。
You could also use CancelAfter()
to implement your 50s timeout, switch from BeginGetResponse..EndGetResponse
to GetResponseAsync
etc. 您还可以使用CancelAfter()
来实现您的50秒超时,从BeginGetResponse..EndGetResponse
切换到GetResponseAsync
等。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.