将任何给定函数转换为可等待任务
本文关键字:等待 任务 转换 函数 任何 | 更新日期: 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;
}