encapsulating on-the-fly Threads and Timers

There are two things I find myself typing a lot, which require multiple lines of code. I would like to have helper methods that do the same thing but require only one line of code.

  1. Perform something in a thread (in order not to lock up the main thread, where myMethod has a parameter “sender as Thread”)

dim myThread as new Thread AddHandler myThread.Run, WeakAddressOf myMethod myThread.Run

What I would like to write is:

Perform_InThread( WeakAddressOf myMethod )
  1. Call a method from a timer (where myMethod has a parameter “myTimer as Timer”, usually I’m writing this within a thread, in order not to throw a ThreadAccessingUIException)

dim myTimer as new Timer AddHandler myTimer.Action, WeakAddressOf myMethod myTimer.Period = 0 myTimer.Mode = 1

What I would like to write is:

Perform_FromTimer( 0, 1, WeakAddressOf myMethod )

For the second situation, thanks to Christian Schmitz I have CallDelegateOnMainThreadMBS( AddressOf myMethod, param ), but I’ve found no solution for the first situation. I tried creating a method which receives a “WeakAddressOf MethodName” as Ptr and then casts it as a delegate to be used with AddHandler, but it doesn’t work, because Xojo tells me the delegate has to be type Delegate( Thread ) - which doesn’t make sense to me, and which I haven’t been able to figure out how to do with the Ptr either. Any ideas?

For option 2, you can use “xojo.core.timer.callLater”.

I’ve tried using that instead of CallDelegateOnMainThreadMBS, but every time I try it, Xojo gives me the “there are multiple items with this name and it’s unclear to which one this is referring” error, which never makes any sense, and I go back to using the MBS call.

Here’s my 5 minute solution
I’m sure there’s issues with it but it illustrates a way to do this that doesn’t require a plugin

Protected Class SElfImmolatingTimer
Inherits Timer
		Protected Sub Constructor()
		  
		End Sub

		Sub Constructor(what as TimerDelegate, period as integer)
		  Me.mHardref = Self
		  Me.Period = period
		  me.mode = Timer.ModeSingle
		  me.mWhat = what
		  
		  AddHandler Me.action, addressof mDowhat
		  
		  
		End Sub

		Private Sub mDoWhat(which as Timer)
		  If mWhat <> Nil Then
		    mWhat.Invoke(Self)
		  End If
		  Me.mHardref = nil
		End Sub

		Shared Sub PerformInTimer(what as TimerDelegate, period as integer)
		  Dim t As New SelfImmolatingTimer( what, 10 )
		  
		  
		End Sub

		Delegate Sub TimerDelegate(which as Timer)

		Protected mHardRef As SElfImmolatingTimer
		Protected mWhat As TimerDelegate
End Class

Call it like

  SelfImmolatingTimer.PerformInTimer( addressOf foo, 1)

note I use addressof not weak address of because IF the target of the delegate (the method to be called) disappears before you call the method you have no way to detect this and you WILL just get nil object exceptions

using addressof prevents this as it holds a hard reference

Thank you very much, Norman. Looks like that does exactly what I’ve been looking for. Now I just need to refactor your code for a Thread … ( will post code after dinner if successful ).

Should be possible to make that for a thread as well where the shared method creates the instance and sets it up so the thread holds a reference to itself until its run event ends
That way even though your mainline code may drop the reference the thread holds a reference to itself which keeps it from dying until its all done

It works!

Norman, I edited your code a little and added two helper methods in a global Module to get this to work the way I described in the initial post above. Now I can write:

Perform_InThread( AddressOf myMethod )

and

Call_FromTimer( AddressOf myMethod )

I’ve uploaded a demo project which contains these classes along with the global module and helper methods here

I know it may seem like a lot of fuss to get around writing a few lines of code, but to me it’s worth it. If anyone wants to improve on this, please let me know. Thanks again to Norman Palardy (credit given in the example project).

Thank you for sharing. This will be very useful.

P.S. In the first upload, I forgot to put in the Notes that the helper methods I added in the Utility module in the example project aren’t necessary. I only made them to conform to my own style of coding. If you want to use shared methods as in Norman’s post, then you would instantiate like this:

Thread_OD.Perform( AddressOf myMethod )

and

Timer_OD.Perform( AddressOf myMethod )

I’ve updated the example project with this information.

@Aaron Hunt Thanks for sharing the solution!

After pressing the Perform_InThread button, I see the UI updating and then it becomes not responsive and I need to stop the debugger. Does the thread need to get killed/terminated? Am I missing something?

I’m using 2018R1.1 in Win10.

[quote=392783:@Chad Posner]@Aaron Hunt Thanks for sharing the solution!

After pressing the Perform_InThread button, I see the UI updating and then it becomes not responsive and I need to stop the debugger. Does the thread need to get killed/terminated? Am I missing something?

I’m using 2018R1.1 in Win10.[/quote]

I tried disabling “Call_FromTimer” from within “DoThisInAThread” and the UI does not lock up, but defeats the purpose as being able too update the UI from the thread.

Then, I tried adding a timer inline from within DoThisInAThread:

Dim tim As New Timer AddHandler tim.action, AddressOf UpdateUI tim.Mode=1 tim.Period=0 //exception here

I’m stuck here.