在finally块中调用.ConfigureAwait(false)是否安全?(Is it safe to call .ConfigureAwait(false) in finally block?)

编程入门 行业动态 更新时间:2024-10-28 07:32:29
在finally块中调用.ConfigureAwait(false)是否安全?(Is it safe to call .ConfigureAwait(false) in finally block?)

我有一个“休息客户端”包装HttpClient ,其方法是异步的。 除了其他原因,我需要使用我的其他客户端控制登录/注销过程,以便不超过会话数。

其余客户端实现IDisposable ,在处理客户端时,我需要检查客户端是否“仍然登录”并注销是否存在。 因为在Dispose方法中进行任何类型的外部调用被认为是不好的做法,所以我有以下内容

public class MappingsController : RestController { [HttpGet] public async Task<HttpResponseMessage> GetYears() { return await ProcessRestCall(async rc => await rc.GetYearsAsync()); } } public class RestController : ApiController { protected async Task<HttpResponseMessage> ProcessRestCall<T>(Func<RestClient, Task<T>> restClientCallback) { RestClient restClient = null; try { var credentials = GetCredentialsFromRequestHeader(); if (credentials == null) { return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Missing credentials from header!"); } var username = credentials["Username"]; var password = credentials["Password"]; restClient = new RestClient(username, password); var authenticated = await restClient.SignInAsync(); if (!authenticated) { return CreateErrorResponseWithRestStatus(HttpStatusCode.Unauthorized, restClient); } var result = await restClientCallback(restClient); // Following works, but since I need to do it in finally block in case exception happens, perhaps It should be done in finally anyways... //await restClient.SignOutAsync(); var response = Request.CreateResponse(HttpStatusCode.OK, result); return response; } catch (Exception e) { return CreateErrorResponseWithRestStatus(HttpStatusCode.BadRequest, restClient, e); } finally { if (restClient != null) { if (restClient.IsSignedIn) { //var signedOutOk = restClient.SignOutAsync();//.Result; //<-- problem - this blocks!!! restClient.SignOutAsync().ConfigureAwait(false); // seems to work, but I am not sure if this is kosher + I can't get return var //Logger.Warn(CultureInfo.InvariantCulture, m => m("Client was still signed in! Attempt to to sign out was {0}", signedOutOk ? "successful" : "unsuccessful")); } restClient.Dispose(); } } } }

I have a "rest client" that wraps HttpClient and whose methods are async. Besides other reasons, I need to control signin/signout process with my rest client so that number of sessions is not exceeded.

The rest client implements IDisposable and upon disposing the client I need to check if the client is "still signed in" and sign out if it is. Since doing any kind of external calls in Dispose method is considered bad practice, I have something as following

public class MappingsController : RestController { [HttpGet] public async Task<HttpResponseMessage> GetYears() { return await ProcessRestCall(async rc => await rc.GetYearsAsync()); } } public class RestController : ApiController { protected async Task<HttpResponseMessage> ProcessRestCall<T>(Func<RestClient, Task<T>> restClientCallback) { RestClient restClient = null; try { var credentials = GetCredentialsFromRequestHeader(); if (credentials == null) { return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Missing credentials from header!"); } var username = credentials["Username"]; var password = credentials["Password"]; restClient = new RestClient(username, password); var authenticated = await restClient.SignInAsync(); if (!authenticated) { return CreateErrorResponseWithRestStatus(HttpStatusCode.Unauthorized, restClient); } var result = await restClientCallback(restClient); // Following works, but since I need to do it in finally block in case exception happens, perhaps It should be done in finally anyways... //await restClient.SignOutAsync(); var response = Request.CreateResponse(HttpStatusCode.OK, result); return response; } catch (Exception e) { return CreateErrorResponseWithRestStatus(HttpStatusCode.BadRequest, restClient, e); } finally { if (restClient != null) { if (restClient.IsSignedIn) { //var signedOutOk = restClient.SignOutAsync();//.Result; //<-- problem - this blocks!!! restClient.SignOutAsync().ConfigureAwait(false); // seems to work, but I am not sure if this is kosher + I can't get return var //Logger.Warn(CultureInfo.InvariantCulture, m => m("Client was still signed in! Attempt to to sign out was {0}", signedOutOk ? "successful" : "unsuccessful")); } restClient.Dispose(); } } } }

最满意答案

使用.ConfigureAwait(false)是一个非问题。 你根本没有等待任务。 由于您没有await它,因此await配置要做什么并不重要。

你正在做的只是基本的火灾和忘记(你可能接受或不接受)。

您应该删除ConfigureAwait(false)无论如何,只是因为它什么都不做并且让读者感到困惑。 如果您可以发送退出请求但实际上没有注销,那么这没关系。

如果需要确保restClient.Dispose(); 在签出请求返回之前不会被调用,那么你有一点......问题。 问题源于退出请求可能不成功,或者更糟糕的是,它可能根本不响应。 你需要一些方法来解决这个问题。

你不能在finally块中使用await ,但你可以通过continuation来或多或少地模仿它的行为。 您可能需要执行以下操作:

public static async Task DoStuff() { IDisposable disposable = null; try { } finally { var task = GenerateTask(); var continuation = Task.WhenAny(task, Task.Delay(5000)) .ContinueWith(t => { if (task.IsCompleted) //if false we timed out or it threw an exception { var result = task.Result; //TODO use result } disposable.Dispose(); }); } }

请注意,由于您没有使用await ,因此DoStuff返回的任务将在第一次触及finally块时立即指示它已“完成”; 不是当继续射击并且物体被丢弃时。 这可能是也可能是不可接受的。

The use of .ConfigureAwait(false) is a non-issue. You aren't awaiting on the task at all. Since you don't await it, it doesn't matter what await is configured to do.

What you're doing is just basic fire and forget (which may or may not be acceptable for you).

You should remove the ConfigureAwait(false) no matter what, just because it does nothing and is confusing to the reader. If it's okay for you to send the request to sign out but not actually sign out, then this is okay.

If you need to ensure that restClient.Dispose(); isn't called until the sign out request returns, then you have a bit of a...problem. The problem stems from the fact that the sign out request might be unsuccessful, or much worse, it might not respond at all. You'd need some way of dealing with that.

You can't use await in a finally block, but you can more or less mimic its behavior through continuations. You may need to do something like this:

public static async Task DoStuff() { IDisposable disposable = null; try { } finally { var task = GenerateTask(); var continuation = Task.WhenAny(task, Task.Delay(5000)) .ContinueWith(t => { if (task.IsCompleted) //if false we timed out or it threw an exception { var result = task.Result; //TODO use result } disposable.Dispose(); }); } }

Note that since you aren't using await the task returned from DoStuff will indicate that it is "done" as soon as it hits the finally block for the first time; not when the continuation fires and the object is disposed. That may or may not be acceptable.

更多推荐

本文发布于:2023-07-22 07:01:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1217938.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:false   ConfigureAwait   finally   block   call

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!