将任何给定函数转换为可等待任务

本文关键字:等待 任务 转换 函数 任何 | 更新日期: 2023-09-27 18:08:10

下面代码的目标是将任何给定函数强制转换为可等待函数。我们的想法是在从数据库中获取数据时使用它,使代码能够灵活地使用同步获取函数(我当前ORM的强制要求),或者使用与async相同的函数。

我知道代码背后的概念可能有很多错误。到目前为止,我只是试图摆脱编译器错误,这样我就可以运行代码并检查行为。当然,我愿意事先讨论这个概念,如果它背后的整个想法是错误的,那么利用我的时间更有效地寻找另一个解决方案。

async static void Main()
{
    // The following line gives a compiler error:
    // Error    1   The best overloaded method match for 'CastFuncToTask<int>(System.Func<int>)' has some invalid arguments 
    int task = await CastFuncToTask<int>(TestFunc(2));
}
private static Task<T> CastFuncToTask<T>(Func<T> func)
{
    TaskCompletionSource<T> taskCompletionSource = new TaskCompletionSource<T>();
    T result = func.Invoke();
    taskCompletionSource.SetResult(result);
    return taskCompletionSource.Task;
}
private static int TestFunc(int testInt) 
{
    return testInt * 2;
}

将任何给定函数转换为可等待任务

运行。net 4.5,可以通过以下方式大大简化代码:

int task = await Task.FromResult(TestFunc(2));

不需要自己用TaskCompletionSource来包装。

我知道这个概念可能有很多问题后面的代码。

如果你想做的是异步查询你的数据库,这个解决方案肯定没有帮助。它只是人为地将结果包装在Task中。如果你真的想异步查询数据库,你需要使用数据库提供程序提供的异步方法。

如果您正在使用MySQL并寻找支持异步的驱动程序,请查看Dapper

您可以使用:

await Task.Run(() => obj.functionname());

改为

int task = await CastFuncToTask<int>(()=>TestFunc(2));

在您的代码中,为CastFuncToTask提供的输入是int (TestFunc返回的),但它需要一个委托Func<T>

我的方法是:

public Task<int> SetOffAsync()
{
    return Task<int>.Factory.StartNew(() =>
    { 
        /*do something else*/
        return 42;
    });
}

你可以调用它:

int var = await SetOffAsync();

使用windows窗体应用程序,我确认第一个调用阻塞了第二个调用没有阻塞。因此,最好的解决方案是使用Task.Run()和lambda表达式。

    private async void button1_Click(object sender, EventArgs e)
    {
        button1.Text = "...";
        var result = await Task.FromResult(TestFunc(2));
        button1.Text = result.ToString();
    }
    private async void button2_Click(object sender, EventArgs e)
    {
        button2.Text = "...";
        var result = await Task.Run(() => TestFunc(2));
        button2.Text = result.ToString();
    }
    private int TestFunc(int x)
    {
        System.Threading.Thread.Sleep(5000);
        return x;
    }

编译器不能知道你不想计算TestFunc(2),但使用它作为一个委托,将首先执行方法和任务。FromResult将在Task中包装一个return value。这不是你想要的。

下面的代码可以帮助别人:

注意:GenericMethod是参数名,我们将其作为输入(T1)和输出(T2)为泛型类型的Func传递。任务工厂将提供运行任务所需的访问权限。

    public async Task<T2> GenericAsyncCall<T1, T2>(Func<T1, T2> GenericMethod, T1 input)
    {
        var _output = await Task.Factory.StartNew(() => {
            var output = GenericMethod.Invoke(input);
            return output;
        });
        return (T2)_output;
    }

问题是CastFuncToTask是同步运行的。如果Func是无参数的,你可以这样做:

public static Task<T> ToTask(this Func<T> func) => Task.Factory.StartNew(() => func());

无论如何,我们可以使用TaskCompletionSource并返回我们想要的东西,而不管方法。假设我们用的是Action而不是Func

public static Task<string> ToTask(this Action action)    
{    TaskCompletionSource<string> taskCompletionSource = new 
           TaskCompletionSource<string>();
           string result = null;
           try
           {
               Task.Factory.StartNew(() => action());
           }
           catch (Exception ex)
           {
               result = ex.Message;
           }
           taskCompletionSource.SetResult(result);
           return taskCompletionSource.Task;
       }