0

2 つの再帰的メソッドが次々に続き、それぞれが各ノードのデータを任意のサイズのツリーに生成します。(2 番目は最初の結果に依存するため、2 つを結合することはできません。また、最初の反復はリーフからルートへ、2 番目はルートからリーフへの反復です。)ノードの追加/位置の更新ごとにデータを生成する必要があります。ツリーのステータスが更新されるたびに、キャッシュされたビットマップにツリーを描画する必要があります。

現在、これには BackgroundWorker を使用していますが、ユーザーが多数のノードを追加すると、タスクが UI と同じスレッドで動作しているかのように遅延が発生し始めます。(BackgroundWorker を使用したのはこれが初めてですが、一般的なスレッドの処理には使用しません)

Matt の投稿に一度出くわしました: BackgroundWorker vs background Thread 代わりに System.Thread に戻す必要がありますか、それとも別のオプションでしょうか? プロセスが終了したら、結果のツリーと整数値をメイン スレッドに格納する必要があります。

[Threading]
private void Commence()
{
    if (worker.IsBusy)
    {
        try
        {
            // I was thinking of fixing this with a timer instead
            worker.CancelAsync();
            while (worker.IsBusy) Thread.Sleep(10);
        }
        catch { return; }
    }
    worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
    // int radMax; TreeNodeEx rTNH;
    // Call the two recursive methods
    radMax = GenerateDataFromNodes(rTNH, radMax);
    UpdateRenderRegion(); // draw result to cached bitmap
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // draw bitmap to screen
    if (!e.Cancelled) Invalidate();
}

[Algorithm]
private int GenerateDataFromNodes(TreeNodeEx hierarchy, int max)
{
    // set hierarchy values
    max = GenerateScaleAndRadius(hierarchy, max);
    GenerateStartAndSweep(hierarchy);

    return max;
}
private int GenerateScaleAndRadius(TreeNodeEx hierarchy, int max, int radius = 2)
{
    for (int i = 0; i < hierarchy.Nodes.Count; i++)
    {
        max = GenerateScaleAndRadius((TreeNodeEx)hierarchy.Nodes[i], max, radius+1);
    }
    // generation a
    return max;
}
private void GenerateStartAndSweep(TreeNodeEx hierarchy)
{
    for (int i = 0; i < hierarchy.Nodes.Count; i++)
    {
        // generation b-1
    }
}
private void node_SweepAngleChagned(object sender, EventArgs e)
{
    for (int i = 0; i < ((TreeNode)sender).Nodes.Count; i++)
    {
        // generation b-2
    }
}

[Invalidation]
private void UpdateRenderRegion()
{
    if (rTNH != null)
    {
        renderArea = new Bitmap(radMax * rScale * 2, radMax * rScale * 2);
        Graphics gfx = Graphics.FromImage(renderArea);

        gfx.SmoothingMode = SmoothingMode.AntiAlias;
        // pens, graphicspaths, and pathgradients

        // draw parent first

        // draw generations next using the same structure as the parent
        DrawNodes(gfx, p, rTNH, rScale, w, h);

        gfx.Dispose();
    }
}
4

1 に答える 1

1

これがあなたのコードの問題だと思います。あなたが提案するように大きなツリーがあり、再帰が長いプロセスである場合、Commence()メソッドを再度呼び出すと、待機して実行されThread.Sleep(10)ます。UI の無反応を引き起こします。このようにコードを書き直してください。BackgroundWorkerがキャンセルされた場合、ワーカーが終了するのを待つ代わりに、イベント ハンドラーでCommence()メソッドを呼び出す必要があります。RunWorkerCompletedコードを確認してください:

private void Commence()
{
      if (worker.IsBusy)
      {
           worker.CancelAsync();
      }
      else
      {
           worker.RunWorkerAsync();
      }
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
      BackgroundWorker bgw = (BackgroundWorker)sender;
      if (bgw.CancellationPending)
      {
            e.Cancel = true;
            return;
      }
      else
      {
            radMax = GenerateDataFromNodes(rTNH, radMax);
            if (bgw.CancellationPending)
            {
                  e.Cancel = true;
                  return;
            }
            UpdateRenderRegion(); // draw result to cached bitmap
      }
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
      if (!e.Cancelled)
           Invalidate();
      else
           Commence();
}

また、補足として、(何らかの理由で) Invalidate が発生すると同時に backBuffer (使用しているビットマップ) にアクセスしないように注意してください。別のスレッドからこのビットマップへのアクセスを既に処理しているかどうかはわかりませんが、それを念頭に置いてください。

于 2012-11-16T18:37:41.210 に答える