[Closed] Background worker updating meshes
it’s interesting article. thanks for pointing to.
in the sample that i shown i use definitely unsafe call. but it doesn’t mean the method doesn’t work. it works but unsafe
the article says: If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state.
I’m using only one thread to call a control.
I haven’t used the Invoke call method. Usually I used ProgressChanged and RunWorkerCompleted events.
both methods execute from the main thread. so the idea is to make everything (all heavy job) in the second thread and fill, paint, invalidate controls in the main one.
following the article it’s not safe to populate ListView/TreeView/DataGrid from second thread for example. but usually it’s the most heaviest part of the filling of the control.
I just need to know how it’s unsafe. If i can guaranty not touching the control anyway during a background worker doing its job, would be safe to fill the control from second thread?
To use listview as an example, it’s perfectly safe to create a collection of listviewitems in one or more background threads and then just .addRange that collection in the UI thread.
do you think it’s safe to create controls in one thread and and them to control created in another? where is a difference?
I’m repeating my question:
If I can guaranty not touching the control anyway during a background worker doing its job, would it be safe to fill the control from second thread?
The answer seems to be: yes, you can.
fm = dotnetobject "form"
lv = dotnetobject "listview"
lv.view = lv.view.details
lv.dock = lv.dock.fill
fm.controls.add lv
lv.columns.add "Index"
bgw = dotnetobject "system.componentmodel.backgroundworker"
fn generateItemsSafe s e =
(
print "generating LV items"
e.result = for i = 1 to 10000 collect
(
dotnetobject "listviewitem" (i as string)
)
)
fn populateLVSafe s e =
(
print "Populating LV"
lv.items.clear()
lv.items.addRange e.result
)
fn populateLVUnsafe s e =
(
for i = 1 to 10000 do
(
lv.items.add (dotnetobject "listviewitem" (i as string))
)
)
--SAFE
--dotnet.addEventHandler bgw "DoWork" generateItemsSafe
--dotnet.addEventHandler bgw "RunWorkerCompleted" populateLVSafe
--UNSAFE
dotnet.addEventHandler bgw "DoWork" populateLVUnsafe
fm.show()
bgw.runworkerAsync()
Some recent experience of mine may be related, as I have data being generated by multiple dotNet threads, which I am then using to update the positions/rotations of objects and script controllers in my scene in real-time.
The process is something like this.
STK.Flight = dotnetobject "LT.ThreeDSinterface"
blah[i].liveData = STK.Flight.SmartContainer 1 -- listens to object's Ethernet traffic
blah[i].liveData.3dsIndex = i
fn blah[i].doStuff sender evtArg = ( if isValidNode blah[sender.3dsIndex].tgtObject do \
( t = sender.position; blah[sender.3dsIndex].tgtObject.pos = [t[1],t[2],t[3]]; ))
dotnet.addEventHandler Blah[i].liveData "UIEvent" (blah[i].doStuff)
-- repeat for bucket loads of objects...
The ‘UIevent’ is new. There was a previous event called ‘dataEvent’ which would trigger when any one of many threads listing to Ethernet traffic updated the data associated with an object. This worked, and objects would update in 3ds Max whenever they moved in the real world. However the longer it ran, and as the number of objects increased, and especially, if they did more complex things in the scene (cameras, script controllers) max would spontaneously hard crash.
So the dotNet boffins at my work, who have barely seen 3ds, and don’t really know what I develop said ‘of course it crashes, you need to use thread invoking , you can’t just barge in on your main UI thread’ (wtf is that was my initial response?) So they gave me a new class called UIThreadDispatcher to include in my dotNet project, and as per their instructions, I wrapped the dataEvent with it, creating UIevents.
Sadly, I can’t re-distribute that class, as it is a generic one used elsewhere in our company’s code.
however, based on my surface level understanding… it loosely ends up doing what you guys mention above.
in c# the difference in the called code in the ‘SmartContainer ’ is below …
if (dataEvent != null)
{
dataEvent(this, new EventArgs());
}
if (UIEvent != null)
{
UIThreadDispatcher.Instance.Invoke(() =>
{
UIEvent(this, new EventArgs());
});
}
The ends result of this is that 3ds max now no longer crashes from updating view ports based on these events… Heaps of objects, all with their own events being triggered as required, updating cameras, lights, etc, quite stably.
Not sure if that helps steer directions with the background worker, or just confuses things, or is a completely separate issue…
Cheers
Mikie
‘of course it crashes, you need to use thread invoking , you can’t just barge in on your main UI thread’
This was what I was hoping to accomplish with: with redraw off ( )
Anyone know of a way to do this with maxscript (thread invoking?).
I’m thinking a mutex for updating may work (where the shared resource is the viewport).
As for hardware affecting stability, I’m probably wrong on that, but I’m still getting different results with same versions of max on different computers. It could be many things.
But, the version of max you are running will determine how ‘efficient’ your background threads are. (ref)
A big issue I ran into is if you have a background thread updating the viewport, and it’s working stably, if you try to orbit/pan/zoom the viewport, max crashes. But if you leave it alone and let it run without user interaction, it completes fine. So perhaps: if the user is not updating the viewport, redraw the viewport? I’d like to see thread invoking implemented in pure maxscript. Anyone?