简体   繁体   English

Thread.Sleep永不返回

[英]Thread.Sleep never returns

I have an application that draws somethong on a form (just a test application). 我有一个可以在表单上绘制东西的应用程序(只是一个测试应用程序)。 I faced a problem with Thread.Sleep. 我遇到Thread.Sleep的问题。 I use thread that initiates DrawinArea repaint every 100 milliseconds. 我使用每100毫秒启动DrawinArea重绘的线程。 It works fine, but sometimes this thread cannot return from This.Sleep(100) call. 它工作正常,但有时此线程无法从This.Sleep(100)调用返回。 I've checked in the debugger, and I'm sure that problem is in Thread.Sleep(). 我已经检查了调试器,并且确定问题出在Thread.Sleep()中。

Attention: it requires Gtk# to run. 注意:它需要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();
        }


    }

}

Once faced this problem I decided to switch to System.Timers.Timer, but I've also faced a problem with it. 遇到此问题后,我决定切换到System.Timers.Timer,但是我也遇到了问题。 Timer stops firing event after some time. 计时器会在一段时间后停止触发事件。 I searched for this problem and found that GC can destroy timer if there are no references to it. 我搜索了此问题,发现如果没有任何引用,GC可以销毁计时器。 My timer is a class member, so reference always exists, but in any case, it stops. 我的计时器是类成员,因此引用始终存在,但无论如何,它都会停止。 What is the problem? 问题是什么?

From the documentation of QueueDraw() it stated QueueDraw()的文档中可以QueueDraw()

Equivalent to calling Widget.QueueDrawArea for the entire area of a widget. 等效于为小部件的整个区域调用Widget.QueueDrawArea。

Reading the documentation for QueueDrawArea it states 阅读状态为QueueDrawArea的文档

Invalidates the rectangular area of widget [...]. 使小部件的矩形区域无效[...]。 Once the main loop becomes idle (after the current batch of events has been processed, roughly), the window will receive Widget.ExposeEvent events for the union of all regions that have been invalidated. 一旦主循环变为空闲状态(大致处理完当前一批事件之后),该窗口将收到Widget.ExposeEvent事件,用于所有无效区域的并集。

What is happening is every 100ms you are putting another message on the queue for the main window and the main window is taking longer than 100ms to process this request. 发生的情况是,每100毫秒您将另一条消息放入主窗口的队列中,而主窗口花费的时间超过100毫秒来处理此请求。 Because you are producing redraw requests faster than it can fulfill redraw requests this is flooding the queue and once it reaches its max size no new messages can be received and it "locks up". 因为您生成重绘请求的速度快于它无法满足重绘请求的速度,所以这将泛滥队列,并且一旦达到最大大小,就无法接收到新消息,它会“锁定”。

When you stated you tested this by updating the window's title that update would also need to go on to the queue to get done, however because the queue is full those title changes never showed up. 当您说过通过更新窗口标题来测试时,还需要继续进行更新才能完成更新,但是由于队列已满,所以标题更改从未显示出来。 Sleep was not hanging, it was just your debugging method was broken due to the same underlying problem. 睡眠没有挂起,只是您的调试方法由于相同的潜在问题而中断。

The solution is call QueueDraw() less often. 解决方案是较少调用QueueDraw()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM