【问题标题】:Maya threading causing crashMaya线程导致崩溃
【发布时间】:2015-04-01 03:38:37
【问题描述】:

我已经启动了一个自动保存脚本编辑器脚本(使用 Maya 2014),但它真的很不稳定,如果在保存的同时发生某些事情可能会崩溃。我也刚刚意识到即使不保存也会发生崩溃,所以我试图找出真正的问题是什么,最后几乎没有留下任何代码,但仍然能够复制它。

我对代码的想法是运行一个后台线程,它会在其中循环并每隔一段时间备份脚本,但每秒检查一个值以确保它没有被暂停或取消(取消将停止循环) .

我认为问题与 Maya 中后台线程的工作方式有关,因为如果加载/关闭脚本编辑器窗口或切换渲染视图设置上的选项卡(至少选择 Mental Ray,因为它似乎比默认渲染器需要更长的时间加载选项卡)。我想还有其他方法,但这些方法真的很容易找到。

在 while 循环中将其降低为 time.sleep() 后,我认为它为什么会导致崩溃真的没有意义。我还使用了 while time.time()>startTime+1 的另一个睡眠功能,以确保它不是时间模块,但它仍然会导致崩溃。

如果有人想尝试,这里是删减的代码,一旦你用AutoSave.start() 启动线程,如果你不断加载并关闭脚本编辑器窗口,你最终应该会得到一个运行时错误(即 R6025 pure virtual函数调用)。这可能需要多次尝试,但似乎总是最终会发生。

import threading, time
import pymel.core as pm

class AutoSaveThread(object):
    def __init__( self ):
        thread = threading.Thread(target=self.run, args=())
        thread.daemon = True
        thread.start()
    def run(self):
        while True:
            time.sleep(1)
            print "Open and close the script editor enough times and this will crash"

class AutoSave:
    @classmethod
    def start( self ):
        AutoSaveThread()

我打开了十几个选项卡,因此加载/关闭比我没有打开的时间要长一些,这可能会增加可能发生崩溃的时间窗口。

作为记录,这是 Maya 中内置的一段代码,只要关闭脚本编辑器窗口,它就会始终运行。我认为这可能与我的修改版本保存有关,然后尝试同时保存,但它仍然崩溃,循环中没有发生任何事情。

global proc syncExecuterBackupFiles(){
    global string $gCommandExecuter[];
    global string $executerBackupFileName;

    if(`optionVar -q saveActionsScriptEditor`) {
        // clear the script editor temp dir first before writing temp files
        string $scriptEditorTempDir = (`internalVar -userPrefDir` + "scriptEditorTemp/");
        string $tempFiles[] = `getFileList -folder $scriptEditorTempDir`;
        string $file;
        for ($file in $tempFiles) {
            sysFile -delete ($scriptEditorTempDir + $file);
        }

        // save all the executer control text to files
        int $i = 0;
        for($i = 0; $i < size($gCommandExecuter); $i++) {
            cmdScrollFieldExecuter -e -storeContents $executerBackupFileName $gCommandExecuter[$i];
        }
    }
}

【问题讨论】:

    标签: multithreading maya pymel


    【解决方案1】:

    尝试将您对print 的调用封装在pymel.mayautils.executeDeferredmaya.utils.executeDeferred 中,以便在主UI 线程上执行。


    如果您连续加载并关闭脚本编辑器窗口,您最终应该会收到运行时错误(即 R6025 纯虚函数调用)。这可能需要多次尝试,但似乎总是最终会发生。

    我能够在 Maya 2012 上确认此行为,但我怀疑它是特定于版本的。

    我敢打赌,您对 print 的测试调用实际上是导致 Maya 崩溃的原因,因为即使 print 通常只是一条 python 语句,Maya 也有某种挂钩来更新脚本编辑器的输出带有您正在打印的字符串的窗口(可能还有命令响应栏),它们都在主 UI 线程上运行。

    来自Autodesk Knowledge article "Python and threading"

    Maya API 和 Maya Command 架构不是线程安全的。如果 Maya 命令在主线程之外调用,则会引发异常,并且从主线程以外的线程使用 OpenMaya API 会产生无法预料的副作用。

    通过将您的 print 语句传递给 pymel.mayautils.executeDeferred 我(至少到目前为止,谁知道 Maya ;-))无法导致崩溃。

    import threading, time
    import pymel.core as pm
    
    import pymel.mayautils  # like maya.utils, for executeDeferred
    
    # Set to False at any time to allow your threads to stop
    keep_threads_alive = True
    
    def wrapped_print():
        print "Opening and closing the script editor shouldn't make this crash\n"
    
    class AutoSaveThread(object):
        def __init__(self):
            thread = threading.Thread(target=self.run)
            thread.start()
        def run(self):
            while keep_threads_alive:
                time.sleep(1)
                pymel.mayautils.executeDeferred(wrapped_print)
    
    ...
    

    专门包装print 语句的唯一副作用是它不再回显到命令响应栏。如果保留该行为对您很重要,请改用 pymel.mel.mprint

    【讨论】:

    • 非常感谢哈哈,我曾尝试在executeDeferred 中包含其他位,但我没想到要对print 这样做。如果在保存时加载/关闭脚本编辑器,完整的代码仍然会崩溃(怀疑我可以做很多事情但警告人们),但到目前为止它似乎工作正常。只是好奇,我一直在做maya.utils.executeDeferred,不知道pymel.mayautils,有没有比另一个有优势?另外,如果加载了 pymel,pymel.mel.mprint 通常比print 更好用吗?
    • @Peter 您应该能够完成这项工作而不必担心崩溃,但这取决于您从脚本编辑器获取数据的方式和时间。
    • @Peter pymel.mayautils.executeDeferredmel.utils.executeDeferred 的包装器,它还处理 Maya 以批处理模式运行时,print 是 python 语句,而 mprint 调用 maya.mel.eval使用梅尔的印刷品。我链接的 pymel 文档很好地解释了两者的差异,更好的是,两者的来源都可以访问。在您的 Maya 安装中分别查看 Python/lib/site-packages/pymel/mayautils.pyPython/lib/site-packages/pymel/core/language.py
    • 好的,谢谢,我可以从脚本编辑器获取数据的唯一方法是通过cmdScrollFieldExecuter( storeContents=True ),当注释掉它时,可以解决所有崩溃问题,所以我相信只是那个命令才是问题所在.这可能是因为它在 SE 窗口关闭时也被调用,但不幸的是我对此无能为力。
    • @Peter 如果你可以不使用cmdScrollFieldExecuter(storeContents=...),那么它比你自己滚动更容易,否则你可以使用cmdScrollFieldExecuter(query=True, text=True) 来获取字段的内容。
    【解决方案2】:
    import threading
    import time
    import maya.utils as utils
    run_timer = True
    run_num = 0
    def example(interval = 10):
        global run_timer;global run_num;
        def your_function_goes_here():
            print "hello",run_num
            run_num +=1
        while run_timer:
            time.sleep(interval)
            utils.executeDeferred(your_function_goes_here)
    
    t = threading.Thread(None, target = example, args = (1,) )
    t.start()
    
    # stop :
    # run_timer = False
    

    【讨论】:

    • 欢迎来到 Stack Overflow!请不要只用源代码回答。尝试对您的解决方案如何工作提供一个很好的描述。请参阅:stackoverflow.com/help/how-to-answer。谢谢!
    猜你喜欢
    • 2013-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-28
    • 2018-08-12
    • 2011-10-17
    • 2012-12-19
    相关资源
    最近更新 更多