c# - 将同步方法变成异步方法

标签 c# async-await

我正在尝试将一些旧代码的同步方法转换为异步方法,但我在理解时遇到了一些麻烦。从我读过的所有视频和教程来看,他们似乎正在创建两种方法:一种是实际功能,另一种是包装器,然后是在 UI 上调用的包装器。

这是我的代码:

private async Task<bool> login(String username, String password)
{
        var tcs = new TaskCompletionSource<RestSharp.IRestResponse>();

        RestSharp.RestRequest request = new RestSharp.RestRequest("/accounts/login/", RestSharp.Method.GET);
        RestSharp.IRestResponse response = client.Execute(request);

        // Make the login request
        request = new RestSharp.RestRequest("/accounts/login/", RestSharp.Method.POST);
        request.AddParameter("username", username);
        request.AddParameter("password", password);

        response = client.Execute(request);

        // Return loggin status
        dom = response.Content;
        return dom["html"].HasClass("logged-in"); 
}

出于某种原因,当我尝试通过单击按钮调用 UI 线程上的方法时,它要求我将按钮事件设置为async

txtLog.AppendText("Before Await");

Task<bool> result = await login("","");

txtLog.AppendText("After Await");
txtLog.AppendText("Result: " + result.toString());

我是否需要一个也设置为 async 的包装器方法来调用登录?

最佳答案

首先回答你的第二部分,是的,你需要为按钮异步标记事件,如果你想在你的代码中使用关键字await,你必须声明函数 async

第二,如果一个函数使用 async 而没有 await 代码将不会异步运行,你要么需要创建一个任务并运行你的同步方法在其中或将方法重写为异步。

作为任务方法:

private async void button1_Click(object sender, EventArgs e)
{
    txtLog.AppendText("Before Await");

    //Note I changed from "Task<bool>" to "bool", await is like calling ".Result" 
    //  on a task but not blocking the UI, so you store the type you are waiting for.
    bool result = await Task.Run(() => login("","")); //You would still use your old login code before you tried to make it async, it requires no modifications.

    txtLog.AppendText("After Await");
    txtLog.AppendText("Result: " + result.ToString());
}

重写函数方法:

private async void button1_Click(object sender, EventArgs e)
{
    txtLog.AppendText("Before Await");

    //Note I changed from "Task<bool>" to "bool", await is like calling ".Result" 
    //  on a task but not blocking the UI, so you store the type you are waiting for.
    bool result = await login("",""); 

    txtLog.AppendText("After Await");
    txtLog.AppendText("Result: " + result.ToString());
}

private Task<bool> login(String username, String password)
{
    var tcs = new TaskCompletionSource<bool>();

    // Make the login request
    var request = new RestSharp.RestRequest("/accounts/login/", RestSharp.Method.POST);
    request.AddParameter("username", username);
    request.AddParameter("password", password);

    client.ExecuteAsync(request, (response, handle) =>
        {
            try
            {
                // Return loggin status
                var dom = response.Content;

                //dom["html"] did not have a .HasClass in my tests, so this code may need work.
                tcs.TrySetResult(dom["html"].HasClass("logged-in")); 
            }
            catch(Exception ex)
            {
                tcs.TrySetException(ex);
            }
        });

    return tcs.Task;
}

在我的“重写方法”中,我正在使用 ExecuteAsync 女巫是 part of IRestClient .该函数在完成时调用回调方法,在回调方法中我调用了 tcsSetResult 来报告我想要的结果。

您可以通过接收 CancellationToken 来进一步扩展它,如果 token 被引发,则调用 RestRequestAsyncHandle 上的 Abort(),但是如果我们为此,我们需要将 async 带回函数并等待结果,以便我们可以在取消 token 注册后进行清理。

private Task<bool> login(String username, String password)
{
    return login(username, password, CancellationToken.None);
}

private async Task<bool> login(String username, String password, CancellationToken cancelToken)
{
    var tcs = new TaskCompletionSource<bool>();

    // Make the login request
    var request = new RestSharp.RestRequest("/accounts/login/", RestSharp.Method.POST);
    request.AddParameter("username", username);
    request.AddParameter("password", password);

    var asyncHandle = client.ExecuteAsync(request, (response, handle) =>
        {
            try
            {
                // Return loggin status
                var dom = response.Content;

                tcs.TrySetResult(dom["html"].HasClass("logged-in")); 
            }
            catch(Exception ex)
            {
                tcs.TrySetException(ex);
            }
        });

    //if the token is canceled it will call `asyncHandle.Abort()` for us.
    using(cancelToken.Register(() =>
        {
           if(tcs.TrySetCanceled(cancelToken))
               asyncHandle.Abort();
        }))
    {
        return await tcs.Task;
    }
}

关于c# - 将同步方法变成异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18166486/

相关文章:

c# - 如何连接 2 个字节?

c# - 双击数据网格中的链接按钮,它将变为下拉列表

linq - 如何使用 LINQ 等待 ParallelQuery?

c# - 在异步任务和 UI 线程中提高 PropertyChanged

c# - 在 Entity Framework 核心中执行业务规则

c# - 在不调试的情况下将测试日志写入 Visual Studio 输出 Pane ?

c# - SetUserAlarm Okuma THINC API C#

javascript - 无法让异步函数在简单的 NodeJS 脚本中工作

c# - StaTaskScheduler和STA线程消息泵送

c# - 带有异步成员数据的 xUnit 理论