3

希望你们中的一些人可以就这一点给出一些指示。

我生成代码,我必须调用远程资源,如 web 服务或数据库。

考虑这段代码

class Parent{    
    IEnumerable<Child> Children;

    int SumChildren() {  
        // note the AsParallel
        return Children.AsParallel().Sum(c => c.RemoteCall()); 
    }   
}

class Child {        
    public int RemoteCall() {
        // call some webservice. I'd like to pool these calls 
        // without having to rewrite the rest of this code
    } 
}

对于 50 个孩子,它将调用 50 次服务,占用 50 倍的开销。在我现实生活中的例子中,这很容易是一百万个调用,使整个事情变得爬行。

我想做的是以某种对调用线程/任务透明的方式批处理这些调用。因此,它不是直接调用服务,而是调用一些对这些调用进行批处理的中央队列(“火车站”)。

因此,当它这样做时,调用任务会阻塞。然后队列等待 X 调用累积,然后使用请求列表对远程服务进行 1 次调用。

当结果出现时,该队列将返回值返回给正确的任务并解除阻塞。对于调用线程,所有这些都保持隐藏,看起来就像另一个函数调用。

这可以做到吗?TPL 中是否有原语可以让我这样做?

它有点像 CCR 的气味,有很多事情在同时进行,等待其他事情完成。

我当然可以重写此代码以在 Parent 类上创建请求列表,然后调用该服务。问题是我的真正问题是生成了所有这些代码。因此,我必须“深入了解” Child.RemoteCall 的实现,这使得这一切变得比现在复杂得多。此外,孩子可能是远程对象等的代理。如果可行的话,这将非常困难,我宁愿隔离这种复杂性。

希望这对某人有意义,如果不让我知道,我会详细说明。

4

3 回答 3

3

您正在探索大规模并行编程的表面。您需要以面向并发的方式进行思考。您正在启动 51 个作业,而不是您需要批处理的 50 个作业。额外的工作是管理 50 个工作的工作。就您需要的原语而言。

JOBHANDLE X= GetJobId();
//single job
AddJob(JOBHANLDE X,ChildJob y);
//container of jobs
AddJobs(JOBHANDLE x, ChildJobs Y);

BeginAsyncExecute(JOBHANDLE X);
WaitTillResult(JOBHANDLE X);

您需要一个在后台定义阻塞原语(超出 OS 内核提供的原语)并管理要执行的工作线程和作业的引擎,从外观上看,这些引擎是由 PLINQ 技术处理的。PLINQ 还使用了很好的绿色线程。

您已经提到您将混合使用数据库和网络服务器。因此,您的作业流程/功能必须在批处理执行之前将子代映射到正确的资源。因此,50 个孩子可能会减少到更少的可批处理 RPC 调用。

所以你建立你的工作批次,然后你阻止它。

变得更具体将很难。但鉴于到目前为止的讨论,请告诉我你遇到了什么麻烦。

于 2009-12-14T16:46:54.230 回答
1

因此,当它这样做时,调用任务会阻塞。然后队列等待 X 调用累积

如果队列接收到 x 个调用 (x < X),那么调用任务将阻塞,直到另一个任务推送总数 >= X。如果您只有一个任务想要进行 N * x 个调用,它将被卡住。

如果您的应用程序通常有很多任务在运行,那么您可能只会间歇性地看到这个问题——您的负载异常低,或者完全关闭。

您可以通过添加超时来解决此问题,以便如果在时间限制内没有添加任何请求,和/或第一个请求的等待时间超过时间限制,队列将发送批处理请求。

我当然可以重写此代码以在 Parent 类上创建请求列表,然后调用该服务。

也许您使用这种方法走在正确的轨道上。您能否找到一种方法,通过委托、继承、lambda 方法或增强您的生成器,将生成的方法实现替换为手动编码的实现?


...对于我真正的问题,所有这些代码都是生成的。

我不太清楚的一点是生成了哪些代码部分(难以修改)以及可以修改哪些部分代码来解决这个问题?

  1. Child.RemoteCall()
  2. Parent.SumChildren()
  3. 以上都不是。

如果以上都不是,那么您必须能够修改某些内容才能解决问题。父子实例是由 AbstractFactory 构建的吗?如果是这样,那么可以向子实例插入一个代理,该代理可用于修改其行为的非功能方面。

于 2009-12-05T12:22:10.890 回答
0

(使用空格的答案框)

谢谢你的想法。

“这将使...”:我处理从存储库生成的代码。在处理此问题的手工编码示例时,开发人员可以发现并改进它。通过生成代码,很难从一组问题示例中提取一般情况。这可能会变得相当复杂,所以我的策略是分而治之。如果我必须查看走出门的孩子的功能。

“我找到了这个链接……”:我看过 Futures,但这更像是一种分叉机制,当有空闲线程时可以并行化。

TPL 似乎是将工作分成小块。我想要做的是将其中一些位以不同的组合放在一起一段时间,然后将它们再次拆分以进行并行执行。(我想,还在精神上咀嚼这个……)

“再想一想”:再一次,分而治之的策略让我走到了这一步。因此,我将较大的问题分成小块,解决它们,然后将这些小块重新组合在一起。我喜欢想象一个蚂蚁群落,其中每只蚂蚁都遵循简单的规则(或看板,这是一个类似的原则),而不是一些中央管理的东西(查询优化器),因为它很快变得非常复杂,所以很快就会陷入困境。

当父级可以以可并行化的方式调用 50 个子级时,然后这些单独的任务可能会因为它们指向相同的远程资源而被批处理在一起,这会很棒。

这里的主要障碍是我如何让调用任务(或线程或其他什么,一个执行单元)阻塞,让另一个任务为一批任务拾取工作,做到这一点,将答案放入所有任务的集合中把他们的工作,他们再次唤醒他们。(然后以有效的方式..)。

我想我记得 George Chrysanthakopoulos(创建 CCR 的那个人)说过收益回报声明是他习惯于这样的事情。我会尝试在 Channel9 上再次找到那个采访。

问候 GJ

于 2009-12-07T12:16:35.887 回答