简体   繁体   English

如何在当前线程上启动任务(强制内联)

[英]How to start a Task on the current thread (force inlining)

Here's the situation: I have a computation pipeline, in a WPF application, which is full of small nodes that makes different calculations. 情况是这样的:我在WPF应用程序中有一条计算管道,其中充满了进行不同计算的小节点。 It is in fact a tree of those nodes which computations depends on the results of other nodes' computations. 实际上,这些节点的树取决于其他节点的计算结果。 Each node, when its dependencies changes, will trigger a recomputation by starting a task. 当每个节点的依存关​​系更改时,将通过启动任务来触发重新计算。 So if I have a 1000 nodes in my graph. 因此,如果我的图形中有1000个节点。 When one of the nodes changes at the bottom of the tree, it could happen that all the nodes need to recompute, each starting a task and waiting for the results of its child nodes to complete. 当一个节点在树的底部更改时,可能会发生所有节点都需要重新计算的情况,每个节点都开始一个任务并等待其子节点的结果完成。

Symptoms: In certain situations, the computation pipeline seems to be "hanging" and taking a very long time to do simple calculation (normally would take less than a second, but could take from 30 seconds to 15 minutes) By profiling, I noticed that the CPU is quite available, and that all the threads are simply waiting for the results of the child nodes. 症状:在某些情况下,计算管道似乎“挂起”,并且花费很长时间进行简单计算(通常会花费不到一秒钟的时间,但可能会花费30秒到15分钟)。通过分析,我注意到CPU非常可用,并且所有线程都只是在等待子节点的结果。 No one is doing calculation when having a breakpoint. 有断点时,没人在做计算。

In my limited knowledge of the ThreadPool and TaskScheduler, is seems that the tasks that would be doing work are just far away in the queue, and thus everyone is just waiting. 据我对ThreadPool和TaskScheduler的有限了解,似乎正在执行任务的任务排在很远的队列中,因此每个人都在等待。 Doesn't look like a deadlock, since it will resume at some point. 看起来不像僵局,因为它将在某个时刻恢复。 I guess I need to start less tasks, or to boost the ThreadPool's number of min thread to like 400 and then the problem disappears (but I prefer obviously the first solution) 我想我需要开始更少的任务,或者将ThreadPool的最小线程数增加到400,然后问题消失(但我显然更喜欢第一个解决方案)

Here's a quick glance on how I request the node's result (not actual code, since mine is bigger with thread safety and basic plumbing). 快速浏览一下我如何请求节点的结果(不是实际的代码,因为我的线程安全性和基本管道功能更大)。

public T GetOrComputeValue()
{
    return GetOrComputeValueAsync().Result;
}

public Task<T> GetOrComputeValueAsync()
{
     // If we are not flagged as dirty, then we can return the last 
     // computation-task, which is either waiting to be started yet, 
     // still busy computing or might already have finished long ago. 
     if (!IsDirty && (_computationTask != null))
         return _pendingRecomputationTask ?? _computationTask;

     IsDirty = false;
     _computationTask = Task.Run( _computationFunc);
 }

So notice that a synchronous call will just call the Async version, which starts a new Task and wait for the result. 因此请注意,同步调用将仅调用异步版本,该版本将启动新的Task并等待结果。 We made it like this so that if we have a synchronous "Get" call, followed by an Async call (before the sync call finished), we want to return the Synchronous Task's result. 我们这样做是为了,如果我们有一个同步的“ Get”调用,然后是一个Async调用(在同步调用完成之前),我们想返回“同步任务”的结果。

The basic usage is from the UI Thread, we call the Async version of the top Computation Nodes (very few calls) and those nodes, in Tasks, will call the Synchronous version. 基本用法来自UI线程,我们调用顶部计算节点的Async版本(很少调用),而在Tasks中,这些节点将调用Synchronous版本。

So, the origin of my question: 所以,我的问题的由来:
- Let's say a node's Task is already in a different thread than the UI thread, and it requests the result of a child node, can I ask that this child's task be inlined in the current thread instead of scheduling it? -假设某个节点的Task已经与UI线程不在同一线程中,并且它请求子节点的结果,我是否可以要求将该子任务插入到当前线程中而不是对其进行调度? Thus reducing the number of tasks sent to the TaskScheduler? 从而减少发送给TaskScheduler的任务数量?

Or any other ideas? 还是其他想法? Or I'm missing the point completely?! 还是我完全错过了重点?

The fact that you are sending allot of tasks to the scheduler is not the problem, the problem is that you are (if I understand your scenario correctly) letting asynchronous code block synchronously to wait for the result of another asynchronous call, which in fact could cause a deadlock if your thread pool gets depleted. 您正在将任务分配发送到调度程序这一事实不是问题,问题在于(如果我正确地理解了您的方案)您正在让异步代码块同步等待另一个异步调用的结果,实际上这可能如果您的线程池耗尽,则会导致死锁。

My recommendation would be to make _computationFunc be of type Func<Task<T>> and it would immediately eliminate the need to call the synchronous GetOrComputeValue within a child node. 我的建议是使_computationFunc的类型为Func<Task<T>> ,它将立即消除在子节点内调用同步GetOrComputeValue的需要。 If the child (leaf) node has no need to be an asynchronous calculation you could simple return its result with Task.FromResult 如果子(叶)节点不需要进行异步计算,则可以使用Task.FromResult简单返回其结果。

I would also suggest you use the async and await coding pattern to make your life much easier. 我还建议您使用asyncawait编码模式来简化您的生活。

EDIT: Regarding to your question about inlining the tasks on the current thread: in my opinion you should not be needing to if you follow my suggestion. 编辑:关于内联当前线程上的任务的问题:在我看来,如果您遵循我的建议,则不需要。 But in order to inline a task would require writing your own task scheduler , but even then you will only be able to inline tasks that are not executing already (you cannot inline something that is already running) 但是要内联任务,需要编写自己的任务调度程序 ,但是即使如此,您也只能内联尚未执行的任务(您不能内联已经在运行的任务)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM