2015-01-10 54 views
0

我有一个应用程序,在窗体上绘制一些(只是一个测试应用程序)。我遇到了Thread.Sleep的问题。我使用每100毫秒启动一次DrawinArea重绘的线程。它工作正常,但有时此线程不能从This.Sleep(100)调用返回。我检查了调试器,并且我确定这个问题出现在Thread.Sleep()中。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可以销毁定时器。我的计时器是一个类成员,因此参考总是存在,但无论如何,它会停止。问题是什么?

+1

我想不出一件GTK#可以做到这一点,将阻止'Thread.Sleep'调用返回。你说你确定这是睡眠呼叫,你是怎样使用调试器来发现这个问题的? –

+0

首先,永远不要为UI线程使用'thread.Sleep'。正如你提到的问题,切换到*计时器*路线,并问你的问题。 –

+0

@ L.B Thread.Sleep不在UI线程中使用,它是并行使用的,使UI重绘。在我的情况下,UI始终是响应式的。 – Xanx

回答

3

QueueDraw()文档它说

相当于调用Widget.QueueDrawArea一个小部件的整个区域。

阅读文档QueueDrawArea它指出

部件失效的矩形区域[...]。一旦主循环变为空闲状态(大致处理了当前一批事件之后),该窗口将接收到Widget.ExposeEvent事件,用于已经失效的所有区域的联合。

发生了什么是每100毫秒你正在主窗口的队列上放置另一条消息,并且主窗口花费超过100毫秒来处理此请求。由于您正在生成重绘请求的速度比实现重绘请求的速度快,因此这会淹没队列,一旦达到其最大大小,就不会收到新消息,并且它会“锁定”。

当你声明你通过更新窗口的标题来测试这个更新也需要进入队列才能完成,但是因为队列已满,那些标题更改从未出现过。睡眠没有挂起,只是你的调试方法由于相同的底层问题而被破坏。

解决方法是拨打电话QueueDraw()较少。

+0

我增加了间隔时间,它似乎工作!谢谢!我为此浪费了整整一天的时间,不知道为什么我没有想到自己。 – Xanx

+0

@Xanx,你可能想要实现某种自适应调节,以避免严重的瞬时计算机负载仍然永久锁定线程的情况。 –

+0

整个'Redrawer'方法反映了代码的味道,我不完全确定你正在努力完成什么,但我相信有更好的方法来做到这一点。 –