Thread.Sleep 从不 returns

Thread.Sleep never returns

我有一个应用程序可以在表单上绘制一些东西(只是一个测试应用程序)。我遇到了 Thread.Sleep 的问题。我使用每 100 毫秒启动一次 DrawinArea 重绘的线程。它工作正常,但有时该线程无法从 This.Sleep(100) 调用中 return。我检查了调试器,我确定问题出在 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 可以销毁计时器。我的计时器是 class 成员,因此引用始终存在,但无论如何,它会停止。有什么问题?

QueueDraw() 的文档中说

Equivalent to calling Widget.QueueDrawArea for the entire area of a widget.

正在阅读 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.

发生的事情是,每隔 100 毫秒,您就会在 queue 上向主 window 发送另一条消息,而主 window 处理此请求的时间超过 100 毫秒。因为您生成重绘请求的速度快于它完成重绘请求的速度,所以这会淹没 queue 并且一旦它达到其最大大小就无法接收新消息并且它 "locks up".

当你说你通过更新 window 的标题来测试这个时,更新还需要继续到 queue 才能完成,但是因为 queue 是完整的那些标题更改从未出现过。睡眠不是挂了,只是你的调试方法由于同样的底层问题而被破坏了。

解决方法是减少调用 QueueDraw()