实时更新大量对象(1,000+)的最有效方法

本文关键字:有效 方法 000+ 对象 实时更新 | 更新日期: 2023-09-27 18:31:36

循环访问包含大量项目(1,000 个或更多)的集合并实时更新项目属性的最有效方法是什么?目前,我的程序使用 WriteableBitmap 在画布上为集合中的每个对象绘制图像(尽管我没有看到简单的椭圆之间的性能有任何差异)并将其在屏幕上移动。我试图暂时保持逻辑尽可能简单。

<UserControl.Resources>
    <DataTemplate DataType="{x:Type local:Entity}">
        <Canvas>
            <Image Canvas.Left="{Binding Location.X}"
                   Canvas.Top="{Binding Location.Y}"
                   Width="{Binding Width}"
                   Height="{Binding Height}"
                   Source="{Binding Image}" />
        </Canvas>
    </DataTemplate>
</UserControl.Resources>
<Canvas x:Name="content"
        Width="2000"
        Height="2000"
        Background="LightGreen">
    <ItemsControl Canvas.ZIndex="2" ItemsSource="{Binding Entities}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas IsItemsHost="True" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

[Magic]
public class Entity : ObservableObject
{
    public Entity()
    {
        Height = 16;
        Width = 16;
        Location = new Vector(Global.rand.Next(800), Global.rand.Next(800));
        Image = Global.LoadBitmap("Resources/Thing1.png");
    }
    public int Height { get; set; }
    public int Width { get; set; }
    public Vector Location { get; set; }
    public WriteableBitmap Image { get; set; }        
}

(在上面的实体类中,[Magic] 属性在我的所有属性上实现 INPC)

我尝试使用 System.Timers.TimerSystem.Threading.TimerSystem.Threading.DispatcherTimer 来创建具有不同间隔的循环。所有对象都表现得相当不错,直到我得到集合中的大约 800 个对象,然后它们开始变得断断续续。我也尝试使用标准的foreach循环和Parallel.ForEach循环,并没有真正注意到两者之间的区别。我的循环有更复杂的逻辑,但我让它尽可能简单,直到我弄清楚如何简化流程:

void Timer_Tick(object sender, EventArgs e)
{
    Parallel.ForEach(Entities, entity =>
    {
        entity.Location = new Vector(entity.Location.X + 1, entity.Location.Y);
    });
}

(此外,这不是画布的问题;如果我绘制 10 个项目并制作 1,000 个没有图像的项目,它仍然会变得断断续续。

如何使我的程序更有效地实时处理大型集合?我猜我错过了很多东西,因为这是我第一次处理这种性质的事情。任何建议将不胜感激。

实时更新大量对象(1,000+)的最有效方法

集合最好在每个线程中处理,但也有一些选项:

  • 如果任何进程需要一些长时间且未完全消耗的 CPU 操作,您将在问题中使用您的方法,即每个操作的线程

  • 如果有许多集合的
  • 流程操作时间较短,则最好对所有集合仅使用一个线程

如果在线程之间执行同步,也会对性能产生影响。如果你这样做,而且经常这样做,这可能会减慢速度。

无论如何,总有一个地方可以思考,并且是强制性的 - 用于基准测试处理您的行为的不同方法。后一个选项将帮助您了解多任务工作流。


对于某些轻量级的算术 CPU 密集型操作,您可以随意对所有操作使用线程


每个集合的线程数

List<List<Action>> actions = InitializeActions();
foreach(var list in actions)
{
    var listCopy = list; // Mandatory where execution is deferred.
    // Each collection is stared on different thread from thread pool.
    Task.Factory.StartNew(() =>
    {
        foreach(var action in listCopy)
        {
            action();
        }
    }
}

每事物的线程数

// Single thread executes all the code.
List<List<Action>> actions = InitializeActions();
foreach(var list in actions)
{
    foreach(var action in listCopy)
    {
        action();
    }
}

每个操作的线程数

List<List<Action>> actions = InitializeActions();
foreach(var list in actions)
{
    foreach(var action in listCopy)
    {
        // Spawn a thread on each action.
        Task.Factory.StartNew(action);
    }
}

这当然非常简单和粗糙,但你明白了。