Executing Maya Commands in Secondary Threads

Hi,

it’s been a while since i didnt have a look at the forum!!

I’m back with a question regarding the behaviour of maya commands executed in QThreads.

I’ve read the documentation but it’s not clear enough for me: https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2016/ENU/Maya/files/GUID-9B5AECBB-B212-4C92-959A-22599760E91A-htm.html

Where it clearly says:“Maya API and Maya Command architectures are not thread-safe. Maya commands throw an exception if they are called outside the main thread, and use of the OpenMaya API from threads other than the main one has unforeseen side effects.

But then it also says that executeDeferred() and executeInMainThreadWithResult() are meant to execute the functions you provide when maya is idle, the latter blocking the secondary thread where the call was made until it returns a result.

I’m trying to figure out the best way to execute a list of maya commands in a secondary thread that updates a progress bar in the tool GUI.

After some testing and several Maya crashes i’ve managed to make it work this way:

executeDeferred("stringWithMayaCommandsFunctionCallandArgumentsA")
time.sleep(self.SLEEP_INTERVAL)
executeDeferred("stringWithMayaCommandsFunctionCallandArgumentsB")
time.sleep(self.SLEEP_INTERVAL)
<and so on....>

Needless to say that the SLEEP_INTERVAL has to be big enough so that Maya can enter the idle() state, otherwise Maya will crash because it attempts to execute two tasks at the same time.

Now i dont like this approach that much and my next thought was to try to do it with executeInMainThreadWithResult which would seemingly avoid the need to call “time.sleep()” since the secondary thread is blocked until it returns a result.
Well, this approach crashes Maya… despite my belief according to the documentation… this proves i havent quite understood how those functions work.

is there no “stable” way to call maya commands in a secondary thread? If there is, which is the best way to do this?

[QUOTE=jiceq;29604]Hi,

it’s been a while since i didnt have a look at the forum!!

I’m back with a question regarding the behaviour of maya commands executed in QThreads.

I’ve read the documentation but it’s not clear enough for me: https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2016/ENU/Maya/files/GUID-9B5AECBB-B212-4C92-959A-22599760E91A-htm.html

Where it clearly says:“Maya API and Maya Command architectures are not thread-safe. Maya commands throw an exception if they are called outside the main thread, and use of the OpenMaya API from threads other than the main one has unforeseen side effects.

But then it also says that executeDeferred() and executeInMainThreadWithResult() are meant to execute the functions you provide when maya is idle, the latter blocking the secondary thread where the call was made until it returns a result.

I’m trying to figure out the best way to execute a list of maya commands in a secondary thread that updates a progress bar in the tool GUI.

After some testing and several Maya crashes i’ve managed to make it work this way:

executeDeferred("stringWithMayaCommandsFunctionCallandArgumentsA")
time.sleep(self.SLEEP_INTERVAL)
executeDeferred("stringWithMayaCommandsFunctionCallandArgumentsB")
time.sleep(self.SLEEP_INTERVAL)
<and so on....>

Needless to say that the SLEEP_INTERVAL has to be big enough so that Maya can enter the idle() state, otherwise Maya will crash because it attempts to execute two tasks at the same time.

Now i dont like this approach that much and my next thought was to try to do it with executeInMainThreadWithResult which would seemingly avoid the need to call “time.sleep()” since the secondary thread is blocked until it returns a result.
Well, this approach crashes Maya… despite my belief according to the documentation… this proves i havent quite understood how those functions work.

is there no “stable” way to call maya commands in a secondary thread? If there is, which is the best way to do this?[/QUOTE]

I was wondering… is there any means to know when maya is idle? so that i can do something like:


executeDeferred(funcA)
while not Maya.isIdle():
time.sleep(0.01)
executeDeferred(funcB)

AKAIK, you can only use threads with a subprocess that opens a new maya session in standalone mode.
Maya is not multi-threaded, so while you working in the main Maya application, you cannot execute maya commands in threads.

There are a couple of things you can do to get quasi-threadead behavior, but fundamentally it’s going to be a slog. Maya is 20 years old and the core has not been well adapted to multicore-environments .

One option is it have an idle-time scriptjob which collects thread-safe messages and then executes functions in the main thread: you could for example have an idle-time scriptJob which popped messages out of a python Queue object (which is thread safe) and did functions for you, and then a secondary thread could push those messages onto the thread.

Another option is to have the thread issue all of it’s maya scene operations through executeDeferredInMainThreadWithResult, which will at least in theory be safe. This will however be slow, and probably won’t do what you want because it will more or less behave like a regular synchronous call – just one with lots of overhead.

If your goal is to update a progress bar, you are unfortunately out of luck: The GUI is also on the main thread, so any effort to get at the progress bar from the thread will inevitably mean forcing Maya to idle so it can process the results. Most of the time that means you end up either (a) running like a synchronous call or (b) doing all you work in the thread and then seeing the progress bar jump from zero to done in one go.

About the only things that are really worth doing in maya python threads are fetching data from slow processes, like reading a file or grabbing information via a web socket or the like. With those you can run asynchronously and fire a deferred callback at the end. Fine-grained UI control, unfortunately, is basically just not worth the trouble. Plus, if you accidentally hit the UI or the Maya scene from the wrong thread you can hard-crash Maya or generate completely bogus stack traces which send you off on wild goose chases.

TLDR: probably not worth your time.

Hi Theodox,

thanks for the reply.

The scriptjob option might be interesting for me since i haven’t worked yet with any. It is similar to my following thought: enqueue all the deferred commands and execute them in a thread that sleeps after each execution.

The other option using executeDeferredInMainThreadWithResult has been disappointing because as you say “in theory” it is safe to use but the truth is that it also fails and causes maya to crash. Can’t really rely on it.

There are times when i need to do heavy stuff with maya commands and i dont want to have a progress bar in 100% when the process hasnt yet finished. It gives the artist and anyone else bad impression about the tool so i would try to avoid when possible and if not, use the first option.

you can send updates to a progress bar while you are executing code. are you using the standard maya progress bar, or the Qt Progress Bar?

yes, that’s what i’m doing but i cant get rid of the sleep() call without having a maya crash. I’m using a Qt Progress Bar.

It’s probably best to think about how you can batch your jobs and use a standard callback mechanism to provide UI feedback rather than using threads: since all of the work is happening inside of Maya – if I’m reading you right – you’ll actually be slowing things down to give the appearance of responsiveness, because the GIL is making your work effectively single threaded no matter what it looks like. The idle-script-job queue should be reliable but it will also be adding complexity for no perf gain

you just need to call QApplication.processEvents() to update a progressbar.