Home Technical Talk
The BRAWL² Tournament Challenge has been announced!

It starts May 12, and ends Sept 12. Let's see what you got!

https://polycount.com/discussion/237047/the-brawl²-tournament

[Maxscript] DotNet DataGridView + MultiThreading = error while garbage collection

interpolator
Offline / Send Message
SimonT interpolator
I'm working on a script and observed a weird behavior and wanted to share it with you guys. I deleted everything which isn't necessary - that's why the structure of the script might seems confuse.

Situation
I use a DotNet DataGridView and fill it via MultiThreading (CSharpUtilities.SynchronizingBackgroundWorker) with data.

Problem
When i start the script and do a manual garbage collection afterwards (enter gc() in the script listener and hit enter), i get this error in some cases:
An unknown error occurred while MAXScript was
performing garbage collection.
If you get this error multiple times, recommend restarting max

Test Results
I found several places in the script which i can change to fix the problem. I can't explain why that is, but i want to share the knowledge with you and maybe you can explain it to me or it helps other people in the future.

1. Copy this code and execute it in 3Ds Max
2. Do a manual garbage collection (via gc() in the script listener)
3. You should get the error message (see above)
  1. (
  2. pathsArray = #()
  3. tasksArray = #()
  4. threadsArray = #()
  5. rollout gckick "gckick" width:448 height:400
  6. (
  7. dotNetControl lbxResult "System.Windows.Forms.DataGridView" pos:[8,60] width:432 height:336
  8. fn addThread threadArgumentArray = (
  9. --create thread
  10. myThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
  11. myThread.WorkerSupportsCancellation = true
  12. functionName = threadArgumentArray[1] --the Array is 2dimensional 1st value=function 2nd value=path for scannings
  13. dotnet.addEventHandler myThread "DoWork" functionName
  14. --add threads to management arrays
  15. append tasksArray threadArgumentArray
  16. append threadsArray myThread
  17. --get the thread ID based on the thread array
  18. tid = tasksArray.count
  19. --start the thread
  20. myThread.runWorkerAsync tid
  21. )
  22.  
  23. fn resizeList = (
  24. gckick.height = 600 + 60
  25. --WORKS: gckick.height = 660
  26. )
  27. fn addItemImage = (
  28.  
  29. resizeList()
  30. )
  31.  
  32. on gckick open do (
  33. lbxResult.SelectionMode = (dotNetClass "System.Windows.Forms.DataGridViewSelectionMode").FullRowSelect
  34. --Add Columns
  35. imgCol = dotNetObject "System.Windows.Forms.DataGridViewImageColumn"
  36. imgCol.width = 100
  37. imgCol.image = myImg
  38. lbxResult.columns.add imgCol
  39. infoCol = dotNetObject "System.Windows.Forms.DataGridViewTextBoxColumn"
  40. infoCol.headerText = "Info"
  41. infoCol.width = (lbxResult.width - 100)
  42. lbxResult.columns.add infoCol
  43. --ADD Content
  44. rowID = lbxResult.rows.add()
  45. lbxResult.rows.item[0].cells.item[1].value = @"D:\Simon\Projekte\3D\egosoft\interiors_rooms_ar_engibay.max"
  46.  
  47. --Start a multithreaded function
  48. addThread #(addItemImage,"")
  49. --WORKS: addItemImage()
  50. )
  51. )
  52. createDialog gckick
  53. )

Workaround #1

(Not really a solution because it will disable the multithreading, but at least you don't get the GC error anymore)

1. Restart 3Ds Max
2. Change the line addThread #(addItemImage,"") to addItemImage() in the on gckick open do (-section.
3. Execute the script
4. When i do a manueal garbage collection now, i DON'T get any error

Workaround #2

1. Restart 3Ds Max (and revert all your previous script-changes if you made any)
2. Change the line gckick.height = 600 + 60 to gckick.height = 660 in the resizeList-function.
3. Execute the script
4. When i do a manueal garbage collection now, i DON'T get any error

I have no idea why the GC doesn't drop an error after the change in the resizeList function. But I'm happy that i found a "solution" and hope this post will help future people. Or someone can explain what's going on here.

And i want note a really good read when you want to work in a performant wa y with the dotnet DataGrid: http://msdn.microsoft.com/en-us/library/ha5xt0d9.aspx

Solution #1

Kind of a solution was to not call the resizeList() function directly from the multi threaded function addItemImage() since this seems to produce problems. I used a timer which is started before i call the multithreaded function and this timer checks a global bool-variable. This variable is set to true by the multithreaded function when it's done. Then the timer reacts to the now true variable, calls resizeList() and deactives itself again.
  1. (
  2. pathsArray = #()
  3. tasksArray = #()
  4. threadsArray = #()
  5. fillListThreadDone = false
  6. rollout gckick "gckick" width:448 height:400
  7. (
  8. dotNetControl lbxResult "System.Windows.Forms.DataGridView" pos:[8,60] width:432 height:336
  9. timer tThreadObserver "Timer" pos:[424,24] width:24 height:24 active:false interval:200
  10. fn addThread threadArgumentArray = (
  11. --create thread
  12. myThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
  13. myThread.WorkerSupportsCancellation = true
  14. functionName = threadArgumentArray[1] --the Array is 2dimensional 1st value=function 2nd value=path for scannings
  15. dotnet.addEventHandler myThread "DoWork" functionName
  16. --add threads to management arrays
  17. append tasksArray threadArgumentArray
  18. append threadsArray myThread
  19. --get the thread ID based on the thread array
  20. tid = tasksArray.count
  21. --start the thread
  22. myThread.runWorkerAsync tid
  23. )
  24.  
  25. fn resizeList = (
  26. gckick.height = 600 + 60
  27. --WORKS: gckick.height = 660
  28. )
  29. fn addItemImage = (
  30.  
  31. tThreadObserver.active = true
  32. tThreadObserver.ticks = 0
  33. )
  34.  
  35. on tThreadObserver tick do (
  36. if (fillListThreadDone) then (
  37. resizeList()
  38. fillListThreadDone = false
  39. tThreadObserver.active = false
  40. )
  41. )
  42. on gckick open do (
  43. lbxResult.SelectionMode = (dotNetClass "System.Windows.Forms.DataGridViewSelectionMode").FullRowSelect
  44. --Add Columns
  45. imgCol = dotNetObject "System.Windows.Forms.DataGridViewImageColumn"
  46. imgCol.width = 100
  47. imgCol.image = myImg
  48. lbxResult.columns.add imgCol
  49. infoCol = dotNetObject "System.Windows.Forms.DataGridViewTextBoxColumn"
  50. infoCol.headerText = "Info"
  51. infoCol.width = (lbxResult.width - 100)
  52. lbxResult.columns.add infoCol
  53. --ADD Content
  54. rowID = lbxResult.rows.add()
  55. lbxResult.rows.item[0].cells.item[1].value = @"D:\Simon\Projekte\3D\egosoft\interiors_rooms_ar_engibay.max"
  56.  
  57. --Start a multithreaded function
  58. addThread #(addItemImage,"")
  59. --WORKS: addItemImage()
  60. )
  61. )
  62. createDialog gckick
  63. )
Sign In or Register to comment.