如何在 dm 脚本中关闭图像 window 时停止后台线程

How to stop a background thread when an image window is closed in dm-script

我正在尝试编写一个包含后台线程的 dm 脚本,如以下示例代码所示。在这段代码中,我想在图像 window 关闭时停止后台线程。我认为在这种情况下可能需要一些事件监听器。你能告诉我如何通过关闭图像 window 事件来控制(即停止)后台线程吗?如果您可以建议对我的代码进行一些修改或展示您的示例代码,我将不胜感激。

// $BACKGROUND$
//
Class CBackground : Thread
{
    Number isRunning
    Number imgID
    Image tmpIMG
//
    Void Init( Object self, Number iID ){
        imgID = iID
        tmpIMG := GetImageFromID( imgID ) 
    }
//
    Void StopRunning( Object self ){
        isRunning = 0
    }
//
    Number GetIsRunning( Object self ){
        return isRunning
    }
//
    Void RunThread( Object self ){
        Result("Background thread is starting ......")
        isRunning = 1
        while (isRunning)
        {
            tmpIMG = random()
            sleep(0.5)
        }
        Result(" finished !!" + "\n")
    }
}
//
Void Main(){
    Object cbkg = alloc(CBackground)
    Image IMG := RealImage("test",4,64,64)
    IMG = random()
    IMG.ShowImage()
    IMG.SetWindowSize(512,512)
    cbkg.Init(IMG.GetImageID())
    cbkg.StartThread()
}
//
Main()

您不能从该线程外部停止后台线程,即您不能中断一个线程。为了停止后台线程,相应的代码需要有一个停止条件并自行退出。

为了从另一个线程引导这个停止条件,您需要 "communicate" 以某种方式在两个线程之间。这可以通过许多不同的方式来完成。最简单的方法是使用一个简单的数字变量,该变量由后台 运行 代码检查,但可以通过任何其他 "outside" 代码设置。 .

除了简单的变量,还可以使用一些常用的地方,如 f.e。 全局标签。或者,为脚本语言定义了一些更复杂的线程同步对象,如 signalsmutexessemphores并在此处的帮助文档中进行描述:

如何 外部线程将 'break' 插入后台 运行 线程也可以通过许多不同的方式完成。一种是——如上例所示——通过一些打开的对话框进行用户交互。另一个,正如作者所提到的,是有一些事件监听器代码来触发它。 下面的示例将一个键侦听器附加到图像,以便(将此图像放在最前面并选中)用户可以按 ESC 按钮停止线程。

我正在使用提供的脚本进行最少的修改以显示此内容:

// $BACKGROUND$
//
Class CBackground : Thread
{
    Number isRunning
    Number imgID
    Number keyListenID
    Image tmpIMG
//
    Void Init( Object self, Number iID ){
        imgID = iID
        tmpIMG := GetImageFromID( imgID ) 
        ImageDisplay disp = tmpIMG.ImageGetImageDisplay(0)
        keyListenID = disp.ImageDisplayAddKeyHandler( self, "KeyListenAction" )
    }
//
    Void StopRunning( Object self ){
        isRunning = 0
    }
//
    Number GetIsRunning( Object self ){
        return isRunning
    }

    /////////////////////////////////////////////////////////////////////////////
    Number  KeyListenAction(Object self, ImageDisplay disp, Object keydesc ) 
    {
        number b_keyhandled = 0
        If ( keydesc.MatchesKeyDescriptor("esc") ) 
        {
            disp.ImageDisplayRemoveKeyHandler( keyListenID )
            self.StopRunning()
            Result( "\nSend stopping flag, unregister Key-Listeners" )
            b_keyhandled = 1
        }
        return b_keyhandled;
    }
//
    Void RunThread( Object self ){
        Result("Background thread is starting ......")
        isRunning = 1
        while (isRunning)
        {
            tmpIMG = random()
            sleep(0.5)
        }
        Result(" finished !!" + "\n")
    }
}
//
Void Main(){
    Object cbkg = alloc(CBackground)
    Image IMG := RealImage("test",4,64,64)
    IMG = random()
    IMG.ShowImage()
    IMG.SetWindowSize(512,512)
    cbkg.Init(IMG.GetImageID())
    cbkg.StartThread()
}
//
Main()

但是,有几件事我会采取不同的做法:

  • 使用正确的命令而不是旧的“$$BACKGROUND$$”方法启动后台线程。
  • 尽可能封装成class
  • 添加一些安全检查
  • 同时添加一个windows关闭事件侦听器,这样关闭图像window也会停止线程
  • 添加一些调试代码以显示对象何时创建以及何时从内存中删除
  • 让 keylistener pause/unpause 动作。

这里是一个脚本示例,它使用 window-closed 侦听器来中止任务。

Class CBackgroundWithListeners 
{
    Number isRunning
    Number imgID
    Image tmpIMG
    Number winListenID


    // Constructor and Destructor method for debugging reason
    // Always automatically called when object gets created or removed from memory
    CBackgroundWithListeners(object self) { 
        Result( "\n Creating object " + self.ScriptObjectGetClassName() )
        Result( " with ID: " + self.ScriptObjectGetID() )
    }
    ~CBackgroundWithListeners(object self) { 
        Result( "\n Destroying object " + self.ScriptObjectGetClassName() )
        Result( " with ID: " + self.ScriptObjectGetID() )
    }

    // Init Method
    Void Init( Object self, Number iID ){
        imgID = iID
        tmpIMG := GetImageFromID( imgID ) 
        If ( !tmpIMG.ImageIsValid() )
            Throw( "Image of ID " + imgID + " not found." )
        if ( 0 == tmpIMG.ImageCountImageDisplays() )
            Throw( "Image of ID " + imgID + " has no display." )

        DocumentWindow win = tmpIMG.ImageGetOrCreateImageDocument().ImageDocumentGetWindow()
        if ( !win.WindowIsValid() )
            Throw( "Image of ID " + imgID + " has no window." )

        winListenID = win.WindowAddWindowListener( self, "window_closed:HandleClosedAction;" ) 
    }
//
    Void StopRunning( Object self ){
        isRunning = 0
    }
//
    Number GetIsRunning( Object self ){
        return isRunning
    }
//    
    Void HandleClosedAction(object self, number e_fl, DocumentWindow Win)
    {
        self.StopRunning()
        win.WindowRemoveWindowListener( winListenID )
    }
//
    Void RunThread( Object self ){
        Result("Background thread is starting ......")
        isRunning = 1
        while (isRunning)
        {
            tmpIMG = random()
            sleep(0.5)
        }
        Result(" finished !!" + "\n")
    }
}
//
Void Main(){
    Object cbkg = alloc(CBackgroundWithListeners)
    Image IMG := RealImage("test",4,64,64)
    IMG = random()
    IMG.ShowImage()
    IMG.SetWindowSize(512,512)
    cbkg.Init(IMG.GetImageID())
    cbkg.StartThread()
}
//
Main()