0

我有两个一个接一个的递归方法,每个都为每个节点生成数据到任意大小的树。(我不能将两者结合起来,因为第二个取决于第一个的结果 - 而且,第一次迭代是从叶到根,第二次是从根到叶。)我需要为每个节点添加/位置更新生成数据。每次树状态更新时,我都需要将树绘制到缓存的位图上。

我目前正在为此使用 BackgroundWorker,但是当用户添加大量节点时,事情开始滞后,就好像任务在与 UI 相同的线程上工作一样。(这是我第一次使用 BackgroundWorker 但一般不用于处理线程)

我曾经遇到过马特的帖子: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 回答