我有一个应用程序可以在表单上绘制一些东西(只是一个测试应用程序)。我遇到了Thread.Sleep的问题。我使用每隔100毫秒启动DrawinArea重绘的线程。它工作正常,但有时这个线程无法从This.Sleep(100)调用返回。我已经检查了调试器,并且我确定问题出在Thread.Sleep()中。
注意:它需要Gtk#才能运行。
using System;
using Gtk;
using Cairo;
using System.Threading;
using System.Timers;
public partial class MainWindow: Gtk.Window
{
Thread redrawThread = new Thread(Redrawer);
//System.Timers.Timer timer = new System.Timers.Timer();
static int calls = 0;
public MainWindow()
: base(Gtk.WindowType.Toplevel)
{
Build();
this.drawingarea2.ExposeEvent += OnExpose;
redrawThread.Name = "Redraw Thread";
redrawThread.Start(this);
/*
timer.Interval = 100;
timer.Elapsed += (sender, e) =>
{
drawingarea2.QueueDraw();
};
timer.AutoReset = true;
timer.Start();*/
}
protected void OnDeleteEvent(object sender, DeleteEventArgs a)
{
redrawThread.Abort();
Application.Quit();
a.RetVal = true;
}
static void Redrawer(object wnd) {
var area = (MainWindow)wnd;
while (true)
{
Thread.Sleep(100);
area.QueueDraw();
}
}
int x = 200;
int x_mod = 10;
int y = 150;
int y_mod = 10;
void OnExpose(object sender, ExposeEventArgs args) {
var area = (DrawingArea)sender;
if (x + 10 >= drawingarea2.Allocation.Width || x - 10 < 0)
x_mod = -x_mod;
if (y + 10 >= drawingarea2.Allocation.Height || y - 10 < 0)
y_mod = -y_mod;
x += x_mod;
y += y_mod;
var ny = Math.Abs(y - drawingarea2.Allocation.Height);
using (var ctx = Gdk.CairoHelper.Create(area.GdkWindow))
{
ctx.LineWidth = 9;
ctx.SetSourceRGB(0.7, 0.2, 0.0);
ctx.Arc(x, ny, 10, 0, 2*Math.PI);
ctx.StrokePreserve();
ctx.SetSourceRGB(0.3, 0.4, 0.6);
ctx.Fill();
ctx.GetTarget().Dispose();
}
}
}
一旦遇到这个问题,我决定切换到System.Timers.Timer,但我也遇到了问题。定时器在一段时间后停止触发事件。我搜索了这个问题,发现GC可以在没有引用的情况下销毁定时器。我的计时器是一个类成员,因此引用始终存在,但无论如何,它都会停止。有什么问题?
答案 0 :(得分:3)
来自QueueDraw()
的文件
相当于为小部件的整个区域调用Widget.QueueDrawArea。
阅读QueueDrawArea
陈述的文档
使窗口小部件的矩形区域无效[...]。一旦主循环变为空闲(在大致处理了当前一批事件之后),该窗口将接收Widget.ExposeEvent事件,用于已经失效的所有区域的并集。
正在发生的事情是,每隔100毫秒,您将在主窗口的队列中放置另一条消息,主窗口需要超过100毫秒才能处理此请求。因为您正在生成重绘请求的速度快于它可以完成重绘请求,所以这会充斥队列,一旦达到最大大小,就不会收到任何新消息并且“锁定”。
当你声明你通过更新窗口标题进行测试时,更新还需要继续进行队列才能完成,但是因为队列已满,所以标题更改从未显示过。睡眠没有挂起,只是你的调试方法由于相同的潜在问题而被打破。
解决方案是不经常调用QueueDraw()
。