我有两个一个接一个的递归方法,每个方法为每个节点生成任意大小的树的数据。 (我不能将两者结合起来,因为第二个依赖于第一个结果 - 同样,第一个迭代是从叶到根,第二个迭代是从根到叶。)我需要为每个生成数据节点添加/位置更新。我需要在每次树状态更新时将树绘制到缓存的位图。
我目前正在使用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();
}
}
答案 0 :(得分:1)
我认为这是您的代码的问题。如果你有一棵大树,如你所说,你的递归过程很长,那么当你再次调用Commence()
方法时,它会等待并Thread.Sleep(10)
;产生UI无响应。像这样重写你的代码,如果BackgroundWorker
被取消,它应该在Commence()
事件处理程序中调用RunWorkerCompleted
方法,而不是等待worker完成。检查代码:
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(您正在使用的位图)。我不知道你是否已经处理过来自不同线程的这个位图的访问,但请记住这一点。