12

目前,我有大量 C# 计算(方法调用)驻留在将按顺序运行的队列中。每个计算都将使用一些高延迟服务(网络、磁盘......)。

我打算使用 Mono 协程来允许计算队列中的下一个计算继续,而前一个计算正在等待高延迟服务返回。但是,我更喜欢不依赖 Mono 协程。

是否有可以在纯 C# 中实现的设计模式,使我能够在等待高延迟服务返回时处理额外的计算?

谢谢

更新:

我需要执行大量 (>10000) 任务,每个任务都将使用一些高延迟服务。在 Windows 上,您无法创建那么多线程。

更新:

基本上,我需要一个设计模式来模拟 Stackless Python ( http://www.stackless.com/ )中 tasklet 的优点(如下)

  1. 大量的任务
  2. 如果一个任务阻塞了队列中的下一个任务执行
  3. 不浪费 CPU 周期
  4. 任务之间的最小开销切换
4

10 回答 10

9

您可以使用 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,则线程已结束,调度程序将其从列表中删除。如果为真,则调度程序访问 Current 属性以确定线程的当前状态。如果它是 TimeSpan,则线程希望休眠,调度程序将其移动到某个队列中,当休眠时间跨度结束时,该队列可以刷新回主列表。

您可以使用其他返回对象来实现其他信号机制。例如,定义某种 WaitHandle。如果线程产生其中之一,则可以将其移至等待队列,直到发出句柄信号为止。或者,您可以通过生成等待句柄数组来支持 WaitAll。你甚至可以实现优先级。

我在大约 150LOC 中完成了这个调度程序的简单实现,但我还没有开始写博客代码。这是为了我们的 PhyreSharp PhyreEngine 包装器(不会公开),在我们的一个演示中,它似乎可以很好地控制几百个字符。我们从 Unity3D 引擎中借用了这个概念——他们有一些在线文档从用户的角度来解释它。

于 2009-08-24T16:46:26.723 回答
5

我建议使用线程池使用从任务队列提供的活动任务列表以可管理的批次一次执行队列中的多个任务。

在这种情况下,您的主工作线程最初会将 N 个任务从队列中弹出到活动任务列表中,以分派到线程池(很可能使用QueueUserWorkItem),其中 N 表示不会使线程池过载的可管理数量,沼泽您的应用程序因线程调度和同步成本而停机,或者由于每个任务的组合 I/O 内存开销而占用可用内存。

每当一个任务向工作线程发出完成信号时,您可以将其从活动任务列表中删除,并从任务队列中添加下一个要执行的任务。

这将允许您从队列中获得一组滚动的 N 个任务。您可以操纵 N 来影响性能特征并找到在您的特定情况下最好的。

由于您最终会受到硬件操作(磁盘 I/O 和网络 I/O、CPU)的限制,我想越小越好。在磁盘 I/O 上工作的两个线程池任务很可能不会比一个更快地执行。

您还可以通过将活动任务列表限制为一组特定类型的任务来实现活动任务列表的大小和内容的灵活性。例如,如果您在具有 4 核的机器上运行,您可能会发现性能最高的配置是同时运行四个 CPU 密集型任务以及一个磁盘密集型任务和一个网络任务。

如果您已经有一个任务归类为磁盘 IO 任务,您可以选择等到它完成后再添加另一个磁盘 IO 任务,同时您可以选择调度 CPU 密集型或网络密集型任务。

希望这是有道理的!

PS:你对任务的顺序有什么依赖吗?

于 2009-08-24T01:01:59.467 回答
2

您绝对应该查看Concurrency and Coordination Runtime。他们的一个示例准确地描述了您正在谈论的内容:您调用长延迟服务,并且 CCR 有效地允许其他一些任务在您等待时运行。它可以处理大量任务,因为它不需要为每个任务生成一个线程,尽管如果您要求它会使用您的所有内核。

于 2009-08-26T17:31:46.907 回答
1

这不是多线程处理的常规用法吗?

在此处查看诸如 Reactor 之类的模式

于 2009-08-23T19:45:14.780 回答
1

编写它以使用Async IO可能就足够了。

如果设计中没有强大的结构,这可能会导致令人讨厌的、难以调试的代码。

于 2009-08-23T19:45:46.633 回答
1

你应该看看这个:

http://www.replicator.org/node/80

这应该完全符合您的要求。不过,这是一个 hack。

于 2010-11-23T17:52:44.620 回答
0

关于 .NET 中实现的“反应式”模式(如另一张海报所述)的更多信息;又名“Linq to Events”

http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html

-Oisin

于 2009-08-24T00:19:47.583 回答
0

事实上,如果你使用一个线程来完成一项任务,你就会输掉比赛。想想为什么 Node.js 可以支持大量的连接。使用异步 IO 的几个线程!!!Async 和 await 函数可以帮助解决这个问题。

foreach (var task in tasks)
{
    await SendAsync(task.value);
    ReadAsync(); 
}

SendAsync() 和 ReadAsync() 是异步 IO 调用的伪造函数。

任务并行也是一个不错的选择。但我不确定哪个更快。您可以在您的情况下测试它们。

于 2013-12-28T02:20:38.400 回答
0

是的,当然可以。您只需要构建一个调度器机制,该机制将回调您提供的 lambda 并进入队列。我统一编写的所有代码都使用这种方法,我从不使用协程。我包装使用协程(例如 WWW 的东西)的方法来摆脱它。理论上,协程可以更快,因为开销更少。实际上,他们为一种语言引入了新语法来完成一项相当琐碎的任务,此外,您无法正确跟踪协同程序中的错误,因为您将看到的只是->下一步。然后,您必须实现在另一个线程上运行队列中的任务的能力。但是,最新的 .net 中存在并行功能,您实际上是在编写类似的功能。真的不会有很多代码行。

如果有人有兴趣我会发送代码,不要把它放在我身上。

于 2014-02-20T05:37:10.800 回答