C# 协同程序的设计模式替代方案
目前,我有大量的C#计算(方法调用)驻留在一个队列中,该队列将按顺序运行。每次计算都将使用一些高延迟服务(网络、磁盘…) 我打算使用Mono协程来允许计算队列中的下一个计算继续进行,而上一个计算正在等待高延迟服务返回。然而,我不喜欢依赖于单协同程序 是否有一种设计模式可以在纯C#中实现,使我能够在等待高延迟服务返回的同时处理额外的计算 谢谢 更新: 我需要执行大量(>10000)任务,每个任务都将使用一些高延迟服务。在Windows上,不能创建那么多线程 更新: 基本上,我需要一个设计模式来模拟无堆栈Python()中Tasklet的优点(如下所示)C# 协同程序的设计模式替代方案,c#,design-patterns,mono,continuations,coroutine,C#,Design Patterns,Mono,Continuations,Coroutine,目前,我有大量的C#计算(方法调用)驻留在一个队列中,该队列将按顺序运行。每次计算都将使用一些高延迟服务(网络、磁盘…) 我打算使用Mono协程来允许计算队列中的下一个计算继续进行,而上一个计算正在等待高延迟服务返回。然而,我不喜欢依赖于单协同程序 是否有一种设计模式可以在纯C#中实现,使我能够在等待高延迟服务返回的同时处理额外的计算 谢谢 更新: 我需要执行大量(>10000)任务,每个任务都将使用一些高延迟服务。在Windows上,不能创建那么多线程 更新: 基本上,我需要一个设计模式来模拟
.NET 4.0提供了对以下各项的广泛支持:
这不是多线程处理的常规用法吗 查看一些模式,例如Reactor将其写入使用可能就足够了
这可能导致设计中没有强大结构的代码难以调试。关于.NET中实现的“反应式”模式(如另一海报所述)的更多信息;又名“Linq to Events” -Oisin我建议使用,使用从任务队列中获取的活动任务列表,以可管理的批处理方式一次从队列中执行多个任务 在这种情况下,您的主工作线程最初会将N个任务从队列中弹出到活动任务列表中,以分派到线程池(最有可能是使用),其中N表示一个可管理的数量,不会使线程池过载,不会因线程调度和同步成本而使应用程序陷入困境,或者由于每个任务的组合I/O内存开销而占用可用内存 每当任务向工作线程发出完成信号时,您都可以将其从活动任务列表中删除,并从要执行的任务队列中添加下一个任务 这将允许您从队列中获得一组滚动的N个任务。您可以操纵N来影响性能特征,并找到在您的特定环境中最好的 由于您最终被硬件操作(磁盘I/O和网络I/O,CPU)所束缚,我认为越小越好。在磁盘I/O上工作的两个线程池任务执行速度很可能不会超过一个 您还可以通过将活动任务列表限制为一组特定类型的任务来实现活动任务列表大小和内容的灵活性。例如,如果您在具有4个内核的机器上运行,您可能会发现性能最高的配置是四个CPU绑定任务,同时运行一个磁盘绑定任务和一个网络任务 如果已经有一个任务被分类为磁盘IO任务,则可以选择在添加另一个磁盘IO任务之前等待该任务完成,同时也可以选择调度CPU绑定或网络绑定的任务 希望这有意义
PS:您对任务顺序有任何依赖性吗?您可以使用IEnumerable模拟协作微线程。不幸的是,这对阻塞API不起作用,所以您需要找到可以轮询的API,或者具有可用于发送信号的回调的API 考虑一种方法
IEnumerable Thread ()
{
//do some stuff
Foo ();
//co-operatively yield
yield null;
//do some more stuff
Bar ();
//sleep 2 seconds
yield new TimeSpan (2000);
}
C#编译器将把它分解成一个状态机——但它的外观是一个协作微线程
模式非常简单。您实现了一个“调度器”,它保存所有活动IEnumerator的列表。当它在列表中循环时,它使用MoveNext()来“运行”每个列表。如果MoveNext的值为false,则线程已结束,调度程序将其从列表中删除。如果为true,则调度程序访问Current属性以确定线程的当前状态。如果是时间跨度,线程希望睡眠,调度程序将其移动到某个队列,当睡眠时间跨度结束时,该队列可以刷新回主列表
您可以使用其他返回对象来实现其他信令机制。例如,定义某种WaitHandle。如果线程产生其中一个,则可以将其移动到等待队列中,直到句柄发出信号。也可以通过生成一个等待句柄数组来支持WaitAll。您甚至可以实施优先级
我在大约150LOC的时间里完成了这个调度程序的一个简单实现,但我还没有来得及把代码写在博客上。这是为了我们的PhyreSharp PhyreEngine包装器(不会公开),在我们的一个演示中,它似乎可以很好地控制几百个字符。我们借用了Unity3D引擎的概念——他们有一些在线文档,可以从用户的角度对其进行解释。您一定要查看。他们的一个示例准确地描述了您正在谈论的内容:调用长延迟服务,CCR有效地允许在您等待时运行其他任务。它可以处理大量任务,因为它不需要为每个任务生成一个线程,但如果您要求,它将使用您的所有内核。您应该看看以下内容:
这应该正是你想要的。不过,这是一种黑客行为。事实上,如果你使用一个线程执行一项任务,你将输掉这场游戏。想想为什么Node.js可以支持大量连接。在异步IO中使用几个线程!!!Async和Wait函数可以在这方面提供帮助
foreach (var task in tasks)
{
await SendAsync(task.value);
ReadAsync();
}
SendAsync()和ReadAsync()是用于异步IO调用的伪造函数
也是