【发布时间】:2018-08-28 12:47:03
【问题描述】:
我正在使用 C# 编写线程池。
这个线程池需要支持执行不同类型的函数。
以下是我需要的:
- 将函数及其参数绑定到函子(或称为可调用对象)中,其签名应为 void(void)
- 获取一个
System.Threading.Tasks< TResult >,它代表我们的可调用包的返回值
在 CPP 中,我可以通过一些模板魔法来做到这一点:
template <typename funtype, typename ...argstype>
std::future<typename std::result_of<funtype(argstype...)>::type> async(funtype&& func, argstype&&... args) {
//start function body↓
typedef std::packaged_task<std::result_of<funtype(argstype...)>::type(argstype...)> task_type;
auto task = std::make_shared<task_type>(std::forward<funtype>(func));
// bind to a callable object(functor) with signature void(void)
auto whatINeed= std::bind([task](argstype... args) mutable {
(*task)(std::forward<argstype>(args)...);
}, std::forward<argstype>(args)...);
//and we return the std::future which represents the return value of our package
//in C#, i need to return an Task<TResult>
return task->get_future();
}
在 C# 中,现在我写道:
public Task<TResult> async<TResult>(Delegate func, params object[] args)
{
var stdPromiseXD = new TaskCompletionSource<TResult>();
// the lambda is a callable object with signature void(void)
works.Enqueue(() =>
{
try
{
stdPromiseXD.SetResult((TResult)func.DynamicInvoke(args));
}
catch (Exception ex)
{
stdPromiseXD.SetException(ex);
}
});
// return the Task which equals std::future in CPP
return stdPromiseXD.Task;
}
但是这个C#版本不如CPP版本好。首先它不支持不返回函数,其次 DynamicInvoke 方法在某些情况下可能会非常慢。
那么谁能告诉我如何在 C# 中更优雅地将函数和参数绑定到一个包中?
【问题讨论】:
-
我的第一个问题可能是你为什么要自己写一个线程池? .Net Tasks 有什么不能做的吗?您可能还考虑执行“一直异步”,如下所示:您传入的委托可能知道异步模式。 (因为取消是一项合作任务,让 CancellationToken 等待它很方便)。您也可以在(高阶函数)之前传入函数参数,然后只传入带有捕获参数的函数。线程池库对参数没有做任何事情,所以它一定没有意识到这一点,对吧?
-
在给定约束的情况下给出一个快速的答案:您可能需要一些带有泛型参数的重载。 (async 通常在函数的返回类型之前出现,而你的函数还没有名称?)
-
@DennisKuypers 只是为了好玩,我知道 dotnet 为每个程序提供一个线程池
-
@DennisKuypers 重载....我知道我们可以使用重载,但我认为这并不优雅。我想使用一些漂亮的东西,比如 C++11 中的模板可变长度参数
-
我不知道任何 C# 类型安全的 C++-variadic-style 函数机制。如果您查看此action.cs 源代码,您还会发现各种重载,我假设
Func的作者可能使用过类似的东西。 (当然,它们是委托,而不是函数声明)