2015-01-10 2 views
0

У меня есть приложение, которое рисует что-то на форме (просто тестовое приложение). У меня возникла проблема с Thread.Sleep. Я использую thread, который инициирует рисование DrawinArea каждые 100 миллисекунд. Он работает нормально, но иногда этот поток не может вернуться из вызова 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

Во-первых, никогда не используйте 'thread.Sleep' для потоков пользовательского интерфейса. Как вы упомянули в вопросе, перейдите на маршрут * timer * и задайте свою проблему. –

+0

@ L.B Thread.Sleep не используется в потоке пользовательского интерфейса, он используется параллельно, что делает UI для перерисовки. Пользовательский интерфейс всегда отзывчивый в моем случае. – Xanx

ответ

3

Из документации QueueDraw() он заявил

Эквивалент вызова Widget.QueueDrawArea для всей области виджета.

Чтение документации для QueueDrawArea говорится

Аннулирует прямоугольную область виджета [...]. Как только основной цикл становится бездействующим (после того, как текущая партия событий была обработана, грубо), окно получит Widget.ExposeEvent для объединения всех регионов, которые были признаны недействительными.

Что происходит, каждые 100 мс вы помещаете другое сообщение в очередь для главного окна, а главное окно занимает больше 100 мс для обработки этого запроса. Поскольку вы выполняете запросы на перерисовку быстрее, чем могут выполнять запросы перерисовывания, это наводняет очередь, и как только она достигает своего максимального размера, никакие новые сообщения не могут быть получены и «блокируются».

Когда вы заявили, что вы протестировали это, обновив заголовок окна, чтобы обновление также нужно было перейти в очередь для выполнения, однако, поскольку очередь заполнена, эти изменения названия никогда не появлялись. Сон не висел, это был просто ваш метод отладки был нарушен из-за одной и той же основной проблемы.

Решение проблемы с телефоном QueueDraw() реже.

+0

Я увеличил интервал и, похоже, сработал! Спасибо! Я потратил весь день на это и не знаю, почему я сам этого не понял. – Xanx

+0

@ Xanx, вы можете реализовать какое-то адаптивное регулирование для этого, чтобы избежать случаев, когда тяжелая временная загрузка компьютера по-прежнему блокирует поток навсегда. –

+0

Весь метод «Редиректор» пахнет запахом кода для меня, я не совсем уверен, что вы пытаетесь выполнить, но я уверен, что есть лучший способ сделать это. –

Смежные вопросы