DigitalMicrograph下的线程大师

时间:2014-08-26 07:10:23

标签: multithreading dm-script

我在DigitalMicrograph中创建了两个线程,一旦脚本执行就会执行它们 我想要一些不同的东西。

让我们想象一下线程的两个按钮(开始和停止线程) 如何在按下按钮时添加代码以激活线程?

如果您有一个代码示例,那将非常有用。

1 个答案:

答案 0 :(得分:2)

有几件事需要考虑:

  • 您无法在UIframe对象中分配新对象。 (更确切地说:从UI操作调用的方法。您可以在构造函数中或在开始时使用Init()方法分配fe。)因此,您事先分配它们然后让UIframe对象知道它们。

  • 您经常希望UIframe对象知道线程对象,还要知道要识别UIframe对象的线程对象。 (因此,如果线程对象中的某些内容需要,UI可以更改。)

  • 将对象作为对象的成员变量有点危险,因为这些对象只有在保持'对象被释放到。如果两个对象彼此保持为成员变量,那么您处于死锁状态!因此,使用弱引用是保存的:只保留 objectID 数字作为成员变量,并根据需要查看对象。

以下代码示例应该为您提供一个起点。它由2个班级和一个主要电话组成。代码在这个答案中被分开,只是复制&将其粘贴到单个脚本文件中进行测试。

首先是线程对象:

class myThread:Thread
{
  number linkedDLG_ID
  number externalBreak

  myThread( object self )  
  {
    result( self.ScriptObjectGetID() + " created.\n" )
  }

  ~myThread( object self )
  {
    result( self.ScriptObjectGetID() + " destroyed.\n" )
  }

  void SetLinkedDialogID( object self, number ID ) { linkedDLG_ID = ID; }
  void InterruptAtNextChance( object self ) { externalBreak = 1; }

  void RunThread( object self )
  {
    number maxLoop = 30

    object callDLG = GetScriptObjectFromID( linkedDLG_ID )
    externalBreak = 0
    for( number i=0; i<maxLoop; i++ )
    {
      sleep( 0.1 )
      Result( i + "\n" )
      if ( callDLG.ScriptObjectIsValid() )
      {
        callDLG.DLGSetProgress( "progress", (i+1)/maxLoop )
        callDLG.ValidateView()
      }

      if ( externalBreak )
        break;
    }

    // Cleanup at end of thread
    if ( callDLG.ScriptObjectIsValid() )
    {
      callDLG.DLGSetProgress( "progress", 0 )
      callDLG.LookUpElement( "DBevel" ).DLGValue( 0 )
      callDLG.ValidateView( )
    }
  }
}
  • 任何线程类都派生自 Thread 类。

  • 该类有两个成员变量。一个将保存UI对象的ID,另一个是允许外部&#39;调用停止正在运行的线程。

  • 前两个方法是构造函数和析构函数。在这个例子中并不真正需要它们,但是在脚本开发期间将它们放入是很好的做法,因为它们将在结果窗口中指示何时创建该类的对象以及何时将其销毁。这有助于跟踪内存泄漏和死锁情况。

  • 接下来的两种方法允许外面的&#39;调用设置两个成员变量。

  • RunThread 方法是任何 Thread 类的核心。它必须是这个签名,因为它会覆盖父类 Thread 的相应方法,我们从中派生类 MyThread 。当调用方法 StartThread()时, RunThread 方法将启动到单独的后台线程中。 ( StartThread()是类 Thread 的方法。)

  • RunThread 中的实际代码分为两部分:

    1. &#39;动作循环&#39;做你想做的任何事情,但如果布尔变量改变值,允许快速退出。这就是外部调用可以中断的方式。这将进一步讨论。

    2. A&#39;清理&#39;对象可以影响UI对象的部分,也在下面讨论。

接下来是UI类:

class myDialog:UIframe
{
  object callThread

  myDialog( object self )
  {
    result( self.ScriptObjectGetID() + " created.\n" )
  }
  ~myDialog( object self )
  {
    result( self.ScriptObjectGetID() + " destroyed.\n")
  }


  TagGroup CreateDLG( object self )
  {
    image i := IntegerImage( "", 1, 0, 25, 25)
    i = 0; i[ 2 , 2 , 23 , 23 ] = 1;
    image onImage, offImage
    onImage   = RGB( 0*i , 200*i , 0*i )
    offImage  = RGB( 200*i , 0*i , 0*i )

    TagGroup tgItems, tg, button, label, progress
    tg = DLGCreateDialog("Dialog",tgItems)
    button = DLGCreateDualStateBevelButton( "DBevel", onImage, offImage, "StartPressed" )
    progress = DLGCreateProgressBar( "progress" ).DLGfill( "X" )
    label = DLGCreateLabel( "start/stop" )
    tgItems.DLGAddElement( DLGGroupItems( button , label ).DLGTableLayout( 2 , 1 , 0 ) )
    tgItems.DLGAddElement( progress )
    return tg
  }

  object Init(object self, number callThreadID )
  {
    // Assign thread-object via weak-reference
    callThread = GetScriptObjectFromID( callThreadID )      
    if ( !callThread.ScriptObjectIsvalid() )
      Throw( "Invalid thread object passed in! Object of given ID not found." )

    // Pass weak-reference to thread object
    callThread.SetLinkedDialogID( self.ScriptObjectGetID() )  
    return self.super.init( self.CreateDLG() )
  }

  void StartPressed( object self )
  {
    number active = self.LookupElement( "DBevel" ).DLGGetValue()
    if ( active )
      callThread.StartThread()
    else
      callThread.InterruptAtNextChance()
  } 
}
  • 任何对话框(UI)类都派生自 UIframe 类。

  • 此类只有一个成员变量:一个对象,它将成为线程对象。

  • 同样有一个构造函数/析构函数方法,以便于调试。

  • CreateDLG 方法构建描述对话框的tagGroup。我不会在这里详细介绍,但实质上它会在显示时创建以下对话框:

    Dialog displayed

  • Init()方法初始化对象。基类 UIframe Init()方法需要描述性TagGroup并返回UI对象本身。我们在扩展的 Init()方法的最后一行调用它,并使用我们的class-method创建tagGroup:

    return self.super.init( self.CreateDLG() )

    之前的代码是将我们的线程对象链接到UI对象的内容。我们传入一个数字,它是我们的线程对象的对象ID。我们现在从内存中获取相应的对象并将其分配给我们的本地成员变量。 (注意:变量现在保存对象本身,而不是它的副本或克隆!)

    callThread = GetScriptObjectFromID( callThreadID )

    立即检查查找是否成功并实际返回了有效对象。如果没有,我们使用抛出的异常停止脚本。从现在开始,UI对象包含&#39;线程对象,可以使用它。

  • 现在出现了反向链接。既然已经分配了UI对象,它也有一个对象ID。我们将这个数字提供给我们的线程对象。

    callThread.SetLinkedDialogID( self.ScriptObjectGetID() )

    从现在开始,线程对象很好地链接到UI对象。回顾 myThread 类,您会注意到我们在RunThread()方法中使用相同的查找和本地存储对象的技巧。

  • StartPressed()是链接到对话框按钮的方法。这个按钮是一个斜角按钮,所以我们查询它的状态,即斜角按钮改变后的状态,并相应地采取行动。我们要么将线程对象的RunThread()方法作为后台线程启动,要么调用相应的&#39;中断&#39;方法,它只是设置布尔变量

最后是主脚本:

void StartScript()
 {
   object threadObject = alloc( myThread )
   object dlgObject = alloc( myDialog ).Init( threadObject.ScriptObjectGetID() )
   dlgObject.display( "Dialog" )
 }
StartScript()
  • 这里不是很多。我们首先创建myThread类的threadObject,然后创建对话框UI对象。

  • 我们使用现有 threadObject的ID初始化对话框对象,然后将其显示为无模式对话框。

需要注意的一些要点:

  • 无论何时在DigitalMicrograph脚本中使用对象变量,都应将它们放入结构块中。当结构块离开时,这可确保对象超出范围并删除。在主脚本中定义和分配的对象变量在脚本末尾被破坏。出于这个原因,我们将主脚本封装在一个方法本身中。

  • 在此示例中,我们使用了两种不同的链接方法:

    • 直接 myDialog 类确实将线程对象本身保留为成员变量。虽然我们仅使用ID初始化它,但我们立即将对象链接到成员变量。

    • 弱引用 myThread 类仅将对话框对象的对象ID保存为成员变量。

    为什么我们这样做?如果 myThread 类将对话框对象保留为成员,那么这两个对象将彼此保持在死锁状态。也不能因为另一个而被破坏。但为什么我们没有对 myDialog 类使用相同的东西呢?因为我们想在后台线程本身中将对话框显示为无模式对话框

    想想主脚本:

    1. 我们创建线程对象
    2. 我们创建对话框对象
    3. 我们显示对话框对象(但我们不会在这里停止脚本执行!
    4. 脚本结束
    5. 但是当脚本结束时,对象变量 threadObject dlgObject 超出范围!它们将立即被破坏,除非有东西将它们留在记忆中 dlgObject 保留在内存中,因为我们将其显示为无模式对话框。当关闭对话框窗口时,它将被释放。但什么保持threadObject?没有!一旦 RunThread()方法完成,它将被释放并随后被破坏。但是,因为它是 dlgObject 的成员,所以它不会被破坏。