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.Timer、およびSystem.Threading.DispatcherTimerを使用して、さまざまな間隔でループを作成しようとしました。コレクション内の約 800 個のオブジェクトに到達するまでは、すべてがかなりうまく動作し、その後、途切れ途切れになり始めます。また、標準foreachループとループを使用してみParallel.ForEachましたが、2 つの違いに実際には気づいていません。私のループにはもっと複雑なロジックがありましたが、プロセスを合理化する方法がわかるまで、できるだけシンプルにしました。

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

(また、これは Canvas の問題ではありません。10 個のアイテムを描画し、1,000 個のアイテムを画像なしで作成すると、それでも途切れ途切れになります。)

プログラムで大規模なコレクションをより効率的にリアルタイムで処理するにはどうすればよいですか? このような性質のものを扱ったのはこれが初めてなので、見逃している部分がかなりあると思います。アドバイスをいただければ幸いです。

4

1 に答える 1

2

コレクションはone-per-threadで処理するのが最適ですが、いくつかのオプションがあります。

  • プロセスが長くて完全に消費されないCPUアクションを必要とする場合は、問題のメソッド、つまりthread-per-actionを使用する必要があります。

  • 処理時間が短いコレクションが多数ある場合は、すべてのコレクションに対して1 つのスレッドのみを使用するのが最適な場合があります。

スレッド間の同期を行うと、パフォーマンスにも影響があります。そうした場合、かなりの頻度で、速度が遅くなる可能性があります。

いずれにせよ、あなたの行動を処理するさまざまな方法をベンチマークするために、常に考える場所があり、必須です。後者のオプションは、マルチタスク ワークフローを理解するのに役立ちます。


いくつかの軽量な算術 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 に答える