【问题标题】:XNA only using 1 core for 4 ThreadPool tasks?XNA 仅使用 1 个内核执行 4 个 ThreadPool 任务?
【发布时间】:2012-11-08 03:28:22
【问题描述】:

我正在创建一个物理模拟,该模拟是真实的,我让它正常工作,但帧率下降得很快。

我正在遍历每个对象,然后再遍历每个对象。

我不确定为什么会这样,因为每帧中的操作数保持不变。我唯一能想到的就是线程是问题所在。我将迭代分为四个部分,我有四分之一的列表是在 4 个单独的线程上计算的,但是当我检查任务管理器时,我实际上只使用了一个内核。

下面是相关代码:

private void Update(GameTime gameTime)
{
    for (int i = 0; i < Bodies.Count; i++)
    {
        Bodies[i].Update(gameTime);
    }

    ThreadPool.QueueUserWorkItem(new WaitCallback(CalculatePhysics0));
    ThreadPool.QueueUserWorkItem(new WaitCallback(CalculatePhysics1));
    ThreadPool.QueueUserWorkItem(new WaitCallback(CalculatePhysics2));
    ThreadPool.QueueUserWorkItem(new WaitCallback(CalculatePhysics3));
}

private void CalculatePhysics0(object o)
{
    for (int i = 0; i < Bodies.Count/4; i++)
    {
        Body body = Bodies[i];
        g.ApplyTo(ref body, Bodies);
    }
}

// 3 other methods exactly the same, but iterating their portion of the list

我对多线程不是很有经验。不过,我可以处理因使用它而产生的问题。我可以看到问题可能是ThreadPool不是达到我想要的效果的好方法,即在线程之间并发遍历列表。

【问题讨论】:

  • 我不认为它是相关的,但我使用的是 xna GS 4.0
  • 谢谢,这非常相关,因为我的答案仅在 4.0+ 中可用。
  • 与您的实际问题无关,但 QueueUserWorkItem 有第二个参数,可让您在“object o”参数中将值传递给函数。这可以让您避免重复的函数,而使用单个代码。
  • @JasonMalinowski 是的,我曾考虑过这样做,但我首先想确保我的方法是准确的。

标签: c# multithreading concurrency xna threadpool


【解决方案1】:

您可以使用Task Parallel Library,它可以帮助您轻松完成大部分工作。这应该在 XNA 4.0+ 中可用

Task.Factory.StartNew(()=>CalculatePhysics0);

我相信默认行为应该可以工作,但您可以指定 TaskCreationOption

如果这不起作用,那么您可以使用TaskScheduler

var scheduler = new TaskScheduler{MaximumConcurrencyLevel=4};
Task.Factory.StartNew(()=>CalculatePhysics0, null, TaskCreationOptions.None, scheduler);

【讨论】:

  • 我已经实现了这个,我没有看到性能提升。此外,任务管理器仍然告诉我,在 12 个内核中只使用了一个。
  • @annonymously 好的,我已经更新了我的答案以使用 TaskScheduler。我很惊讶默认的没有正确分配......它在我过去使用它时有。处理是否快速进行,方法是否共享任何变量...如果不是,则可能是 XNA 部分的限制
  • 我什至无法编译任务调度程序。它说TaskScheduler 类是抽象的,而MaximumConcurrencyLevel 是只读的
【解决方案2】:

关于您在另一个答案上留下的评论:更多线程!= 性能提升。您可能会惊讶有多少事情实际上在串行而不是并行中表现得更好。当然,这并不意味着您的代码不能从不同内核上的多个线程中受益,只是您不应该假设添加更多线程会神奇地导致您的处理吞吐量增加。

很难说什么可以帮助您的代码更好地执行,而无法查看更多内容。首先,了解一点 .NET 中的线程模型会很有帮助。我建议您阅读它,因为我不能在这里详细介绍。 .NET 中的线程不一定是本机线程。也就是说,当你在ThreadPool 中排队第三个物理方法时,第一个可能已经完成,所以它只会使用已经创建的线程。当然,有时您会在另一个任务完成之前将一个任务排队,并且必须创建一个额外的(昂贵的)本机线程。在这种情况下,线程越少越好。

当您查看任务并行库时,抽象的想法会更进一步,其中每个任务可能看起来像一个线程,但实际上离它更远。许多任务最终可以在同一个线程上运行。您可以通过向TaskFactory 暗示这是一个长时间运行的任务来解决此问题,即Task.Factory.StartNew(() =&gt; DoWork(), TaskCreationOptions.LongRunning),这会导致调度程序在新线程上启动此任务(同样,这可能并不比仅仅拥有运行时更好为您安排)。

话虽如此,如果您的工作需要足够的时间来处理,那么如果您使用ThreadPool 对其进行排队,它最终在单独的线程上运行。但是,如果工作进行得足够快,则似乎只使用了一个线程或一个内核。

  • 是什么让您得出您只使用一个内核的结论?
  • 只是您从任务管理器中看到的吗?这几乎不是一个决定性的结果。
  • 您是否尝试在任务管理器的详细信息选项卡中添加一列线程,并实际检查您的代码是否产生了额外的线程?
  • 我还看到您对Bodies 的数组进行了两次迭代,是否有任何特殊原因您不能同时使用GameTime 更新主体(可能是XNA 中的一些限制)?

不过,以上所有内容都只是在黑暗中拍摄。如果您真的,我的意思是真的想知道任何性能问题的根源,您将使用像Ants by RedGatedotTrace by JetBrains 等体面的分析器来分析您的代码,或者如果您拥有高级版或更高版本的 Visual Studio,则分析器直接内置在您的 IDE 中。

我不确定您的问题是否在您认为的位置,根据我的经验,这种情况很少出现。我希望我上面的一些脑筋急转弯可以对你有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-02
    • 1970-01-01
    • 1970-01-01
    • 2014-04-10
    • 1970-01-01
    • 2023-03-07
    相关资源
    最近更新 更多