- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试创建一个通用方法来取消异步 Web 请求以及与之关联的任何其他操作。我发现了另一个与此相关的问题,例如 this .
我已经编写了一个帮助程序类来执行此操作。我在下面介绍:
public static class Helpers
{
public static async Task<string> GetJson(string url,
CancellationTokenSource cancellationTokenSource,
bool useSynchronizationContext = true)
{
try
{
var request = (HttpWebRequest)WebRequest.Create(url);
string jsonStringResult;
using (WebResponse response = await request.GetResponseAsync()
.WithCancellation(cancellationTokenSource.Token,
request.Abort, useSynchronizationContext))
{
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
jsonStringResult = await reader.ReadToEndAsync();
reader.Close();
dataStream.Close();
}
cancellationTokenSource.Token.ThrowIfCancellationRequested();
return jsonStringResult;
}
catch (Exception ex) when (ex is OperationCanceledException
|| ex is TaskCanceledException)
{
}
catch (Exception ex) when (ex is WebException
&& ((WebException)ex).Status == WebExceptionStatus.RequestCanceled)
{
}
catch (Exception ex)
{
//Any other exception
}
finally
{
cancellationTokenSource.Dispose();
}
return default;
}
public static async Task<T> WithCancellation<T>(this Task<T> task,
CancellationToken cancellationToken, Action action,
bool useSynchronizationContext)
{
using (cancellationToken.Register(action, useSynchronizationContext))
{
return await task;
}
}
}
注意线
cancellationTokenSource.Token.ThrowIfCancellationRequested();
就在返回 JSON 字符串之前。
当操作被取消,执行流程上线时
StreamReader reader = new StreamReader(dataStream);
例如,下面的所有行(reader.Close()
等)将被执行,并且当 ThrowIfCancelationRequested()
被执行时将抛出异常 - 是正确的?我错过了什么吗?
如果是这样,有没有办法一次取消所有内容?
感谢大家的回复,
在提供了答案和所有真正有用的评论后,我更新了实现。我使用 HttpClient 和链接中的扩展方法来让一项实际上不能被取消的任务表现得像一个可以取消的任务——readAsStringAsync 实际上被执行了,因为它不能接受取消 token 。
public static class Helpers
{
public static async Task<string> GetJson(string url,CancellationToken cancellationToken)
{
try
{
string jsonStringResult;
using (var client = new HttpClient())
{
cancellationToken.ThrowIfCancellationRequested();
using (var response = await client.GetAsync(url, cancellationToken))
{
jsonStringResult = await response.Content.ReadAsStringAsync().WithCancellation(cancellationToken);
}
}
return jsonStringResult;
}
catch (Exception ex) when (ex is OperationCanceledException)
{
}
catch (Exception ex) when (ex is WebException exception && exception.Status == WebExceptionStatus.RequestCanceled)
{
}
catch (Exception ex)
{
//LogException(ex);
}
return default;
}
public static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
return task.IsCompleted
? task: task.ContinueWith(completedTask => completedTask.GetAwaiter().GetResult(),cancellationToken,TaskContinuationOptions.ExecuteSynchronously,TaskScheduler.Default);
}
}
最佳答案
all lines below (reader.Close() etc.) will be executed and the exception will be thrown when ThrowIfCancelationRequested() is executed - is that correct? Am I missing something?
If so, is there a way to cancel everything at once?
首先,您要取消的操作必须明确支持被取消。因此,您必须尽最大努力使用代码以某种方式以某种方式执行某些操作 CancellationToken
作为参数。如果不可能,那么你就没有其他机会了。
这就是为什么这个概念被称为合作取消的原因。因为几乎总是双方都应该知道发生了取消。客户端应该知道代码实际上被取消了,客户端仅仅知道取消请求是不够的。对于被调用者,重要的是要了解请求取消以正确完成自身的事实。
关于在执行stream
和reader
的Close
方法时检查操作是否被取消。如果要避免内存泄漏,无论是否取消操作,都必须始终调用清理方法。当然,我建议您使用 using
语句,它会自动进行清理。
顺便说一句,为了使某些功能可取消,您不必在执行每一行之前检查是否请求取消。您只需要在执行一些长时间运行的操作之前和之后检查是否请求取消。如果长时间运行的操作支持通过取消 token 属性取消,则传递取消 token 。
此外,您还必须查看副作用。如果您已经招致副作用,您的方法不准备在退出时恢复,这会使您处于不一致状态,请不要取消。
一些通用的代码块可能是这样的:
if(ct.IsCancellationRequested)
{
break; // or throw
}
await DoSomething(ct);
if (ct.IsCancellationRequested)
{
// if there is no side-effect
return; // or throw
// or, we already did something in `DoSomething` method
// do some rollback
}
作为解决方案,您可以使用一些不同的对象,例如 HttpClient
或 WebRequest
来执行异步、可等待和可取消的网络请求。你可以看看that link了解实现细节。
关于c# - 可取消的异步 Web 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58787171/
我们构建了一个基于 Netty/NIO 的服务,我正在考虑将该服务部署到我们的生产环境中。我们部署服务的标准方式是作为 WAR,部署在 Tomcat 中。 当我在这里提出相同的方法时,我得到了“不应该
我是一名优秀的程序员,十分优秀!