【问题标题】:How to stop a background thread when an image window is closed in dm-script在 dm-script 中关闭图像窗口时如何停止后台线程
【发布时间】:2018-06-24 04:01:48
【问题描述】:

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

// $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()

【问题讨论】:

    标签: dm-script


    【解决方案1】:

    这里是一个脚本示例,它使用 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()
    

    【讨论】:

    • 非常感谢您提供有用的建议并展示您有用的 dm-script 示例。通过使用关闭窗口的侦听器来停止后台线程的方法正是我想要实现的。再次感谢您的精彩回答!
    【解决方案2】:

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

    为了从另一个线程引导这个停止条件,您需要在两个线程之间以某种方式“通信”。这可以通过许多不同的方式来完成。最简单的是使用一个简单的数字变量,该变量由后台运行代码检查,但可以通过任何其他“外部”代码进行设置。 An example for this can be found in this answer here.

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

    如何外部线程将“中断”插入后台运行线程也可以通过多种不同方式完成。一种是——如上面的例子——通过一些打开的对话框进行用户交互。另一个,正如作者所提到的,是有一些事件监听器代码来触发它。 下面的示例将一个按键监听器附加到图像,以便(在此图像位于最前面并被选中)用户可以按 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$$”方法启动后台线程。
    • 尽可能的封装到类中
    • 添加一些安全检查
    • 另外添加一个windows closed事件监听器,这样关闭图像窗口也会停止线程
    • 添加一些调试代码以显示对象的创建时间和从内存中删除的时间
    • 让 keylistener 暂停/取消暂停操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-17
      • 1970-01-01
      • 2017-03-29
      • 1970-01-01
      相关资源
      最近更新 更多