3

迭代具有大量项目(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; }        
}

(在上面的 Entity 类中,[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);
    });
}

(此外,这不是 Canvas 的问题;如果我画了 10 个项目并在没有图像的情况下制作了 1000 个项目,它仍然会变得不稳定。)

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

4

1 回答 1

2

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

  • 如果任何进程需要一些长时间且非完全消耗的 CPU 操作,您将在问题中使用您的方法,即thread-per-action

  • 如果有许多进程动作时间短的集合,最好只为所有集合使用一个线程。

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

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


对于一些轻量级的算法 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);
    }
}

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

于 2013-09-17T19:30:47.837 回答