Maxscript: Execute Function WITH Parameters as Multi Threaded processes

I’m playing around with .Net MXS Multi Threading and search a solution for: How to execute a function (multi threaded) with a parameter. Google didn’t help me so far.

Here’s the code which has two problems:
[ol]
[li]The code drops an error after executing the multi thread function (this can be prevented by try/catch)[/li][li]3Ds Max stalls, during execution! This is then of course useless since this shall not happen with multi threading[/li][/ol]


fn doSomeStuff myText = 
(
	for i = 1 to 1000000 do pi * pi * pi
	print myText 
	print "thread done ..."
)

myThread = dotNetObject "system.componentModel.backgroundWorker"	
dotnet.addEventHandler myThread "DoWork" (doSomeStuff "test")
myThread.runWorkerAsync()

– Runtime error: dotNet event handler must be a functions, got: “test”

If you delete the function parameter and the parenthesis, the code works perfectly - no 3Ds Max stalling and no error. But i need to hand over a function parameter! Is this just not possible?

What prevents you from wrapping it in another function, say

dotnet.addEventHandler myThread "DoWork" (fn _ = doSomeStuff "test")

This is black magic! :smiley: But it works, thank you! But i got problems with the variables scope and since i don’t want to use global variables, i looked for another solution and found one. You can hand over an argument when starting the thread with “RunWorkersAsync” and here’s a great example how to use this with arrays:


--------------------------------------------------MULTITHREADING DEFINITION--------------------------------------------------
global  prog_rol, --#(progressform, #(progressbars))
  tasks = #(), --array of tasks, every task'll be executed in separate thread
  finished = 0, --finished tasks count
  senders = #() --task senders

fn progcreate state =
(
 hForm = dotNetObject "System.Windows.Forms.Form"
 hForm.clientsize = dotNetObject "System.Drawing.Size" 500 (20*tasks.count)
 hForm.FormBorderStyle = hForm.FormBorderStyle.FixedToolWindow
 hForm.StartPosition = hForm.StartPosition.CenterScreen
 hForm.TopMost = true
 hform.text = state
 bars = #()
 for i = 1 to tasks.count do
 (
  prog = dotNetObject "System.windows.forms.ProgressBar"
  prog.size = dotNetObject "System.Drawing.Size" 500 20
  prog.location = dotNetObject "System.Drawing.Point" 0 (20*(i - 1))
  prog.value = 0
  hForm.controls.add prog
  append bars prog
 )
 hForm.show()
 progressStart state
 prog_rol = #(hForm,bars)
)
fn progupdate sender task =
(
try --to avoid error if progressform's closed
(
 overall = 0
 for bid = 1 to prog_rol[2].count do
 (
  bar = prog_rol[2][bid]
  if bid == task.userstate then
   bar.value = task.progresspercentage
  overall += bar.value
 )
 progressupdate (overall*100/(tasks.count*100)) --refresh global progress
) catch()
)
fn checkresult =
(
if finished == (tasks.count - 1) then
(
 free tasks
 free senders
 prog_rol[1].close()
 progressend()
 messagebox "All tasks're finished!"
)
else
 finished += 1
)
fn dowork processname =
(
 senders = #()
 if tasks.count > 0 then
 (
  progcreate processname
  finished = 0
  bwthreads = #()
  for task in tasks do
  (
   thread = (dotnetobject "System.ComponentModel.BackGroundWorker")
   thread.WorkerReportsProgress = true
   dotNet.addEventHandler thread "DoWork" task[3]
   dotNet.addEventHandler thread "ProgressChanged" progupdate
   dotNet.addEventHandler thread "RunWorkerCompleted" checkresult
   append bwthreads thread
  )
  for tid = 1 to bwthreads.count do
   bwthreads[tid].RunWorkerAsync tid --run thread with thread ID as parameter
  free bwthreads
 )
)

--------------------------------------------------USER FUNCTIONS--------------------------------------------------
fn testfn1 endval taskid =
(
sender = senders[taskid]
for i = 1 to endval do
(
 --some work
 sender.ReportProgress ((i as float)/endval*100) taskid
 sleep 0.05
)
)
fn testfn1_prototype sender thread =
(
try
(
 task = tasks[thread.argument]
 senders[thread.argument] = sender
 task[1] task[2][1] thread.argument --execute target function with 1 paramater and task ID
)
catch
 sender.ReportProgress 100 thread.argument --finish task on error
)
fn testfn2 endval freezetime taskid =
(
sender = senders[taskid]
for i = 1 to endval do
(
 --some work
 sender.ReportProgress ((i as float)/endval*100) taskid
 sleep freezetime
)
)
fn testfn2_prototype sender thread =
(
try
(
 task = tasks[thread.argument]
 senders[thread.argument] = sender
 task[1] task[2][1] task[2][2] thread.argument --execute target function with 2 paramaters and task ID
)
catch
 sender.ReportProgress 100 thread.argument --finish task on error
)

--------------------------------------------------FUNCTION CALLS--------------------------------------------------
task1 = #(testfn1,#(100),testfn1_prototype) --task syntax: #(<function name>, #(<function parameters>), <function prototype name>)
task2 = #(testfn2,#(50,0.2),testfn2_prototype)
tasks = #(task1,task2,task1,task2) --launch 4 threads(functions can repeat)
dowork "Multithreading test" --launch tasks