【问题标题】:working in python console while executing a boost::python module在执行 boost::python 模块时在 python 控制台中工作
【发布时间】:2013-06-28 10:59:35
【问题描述】:

如何在从 boost::python 模块执行进程时继续使用控制台?我想我必须使用线程,但我想我错过了一些东西。

import pk #my boost::python module from c++
import threading
t = threading.Thread(target=pk.showExample, args=())
t.start() 

这将执行 showExample,它运行一个渲染 3D 内容的窗口。现在我想在这个窗口运行时继续在 python 控制台中编码。上面的示例可以显示窗口,但无法保持控制台交互。任何想法如何做到这一点?感谢您的任何建议。

问候 克里斯

编辑:我还尝试在 showExample() C++ 代码中创建线程,但效果不佳。我可能必须使控制台成为一个线程,但我不知道如何并且找不到任何有用的示例。

Edit2:为了让示例更简单,我实现了这些 c++ 方法:

void Example::simpleWindow()
{
    int running = GL_TRUE;
    glfwInit();
    glfwOpenWindow(800,600, 8,8,8,8,24,8, GLFW_WINDOW);
    glewExperimental = GL_TRUE;
    glewInit();
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    while(running)
    {
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        glfwSwapBuffers();
        running = !glfwGetKey(GLFW_KEY_ESC) && gkfwGetWindowParam(GLFW_OPENED);
    }
}

void Example::makeWindowThread()
{
    boost::thread t(simpleWindow);
    t.join();
}

可能有一些无用的代码行(它只是从我想要使用的真实方法中复制粘贴一部分。)这两种方法都是静态的。如果我在线程中启动交互式控制台并在 python 中启动 pk.makeWindowThread(),我将无法再提供输入。如果我也将 pk.makeWindowThread() 的调用放在 python 线程中,则不起作用。 (我试图在显示窗口时在控制台中打印一些东西。

【问题讨论】:

    标签: python multithreading boost console boost-python


    【解决方案1】:

    当尝试在保持控制台交互的同时执行进程时,请考虑使用subprocessmultiprocessing 模块。在 Boost.Python 中执行此操作时,使用 execv() 系列函数在 C++ 中执行进程可能更合适。

    当试图在保持控制台交互的同时生成 线程 时,必须考虑Global Interpreter Lock (GIL)。简而言之,GIL 是解释器周围的互斥体,防止对 Python 对象执行并行操作。因此,在任何时间点,最多允许一个线程,即获得 GIL 的线程,可以对 Python 对象执行操作。

    对于没有 C 或 C++ 线程的多线程 Python 程序,CPython 解释器用作协作调度程序,从而实现并发。当 Python 知道线程即将执行阻塞调用时,线程将产生控制权。例如,一个线程将释放time.sleep() 内的 GIL。此外,在满足某些条件后,解释器将强制线程让出控制权。例如,一个线程执行了一定数量的字节码操作后,解释器会强制它让出控制权,允许其他线程执行。

    C 或 C++ 线程有时在 Python 文档中称为外来线程。 Python 解释器无法通过释放 GIL 来强制外来线程让出控制权。因此,外来线程负责管理 GIL 以允许与 Python 线程并发或并行执行。考虑到这一点,让我们检查一些 C++ 代码:

    void Example::makeWindowThread()
    {
      boost::thread t(simpleWindow);
      t.join();
    }
    

    这将产生一个线程,thread::join() 将阻塞,等待t 线程完成执行。如果此函数通过 Boost.Python 暴露给 Python,则调用线程将阻塞。由于在任何时间点只允许执行一个 Python 线程,调用线程将拥有 GIL。一旦调用线程在t.join() 上阻塞,所有其他 Python 线程将保持阻塞状态,因为解释器无法强制线程让出控制。为了让其他 Python 线程能够运行,GIL 应该在加入前释放,在加入后获得。

    void Example::makeWindowThread()
    {
      boost::thread t(simpleWindow);
      release GIL // allow other python threads to run.
      t.join();
      acquire GIL // execution is going to occur within the interpreter.
    }
    

    但是,这仍然会导致控制台阻塞等待线程完成执行。相反,考虑生成线程并通过thread::detach() 与它分离。由于调用线程将不再阻塞,因此不再需要在 Example::makeWindowThread 中管理 GIL。

    void Example::makeWindowThread()
    {
      boost::thread(simpleWindow).detach();
    }
    

    有关管理 GIL 的更多详细信息/示例,请考虑阅读this 答案以了解基本实施概述,并阅读this 答案以更深入地了解必须考虑的事项。

    【讨论】:

    • 嗯,这实际上有很大帮助。我现在可以在窗口运行时从 python 控制台中更改窗口的颜色。你说如果我分离线程就不再需要用 GIL 阻塞,但如果我需要我还能这样做吗?例如,当我有可以从 python 写入(更改)并需要从 c++ 读取的数据时。
    • @Chris:这取决于数据存在的位置。如果 Python 正在写入 C++ 内存,而 C++ 线程正在读取 C++ 内存,则需要在 C++ 中使用自己的构造进行同步。另一方面,如果 Python 正在写入 Python 对象,而 C++ 线程正在读取 Python 对象,则 C++ 线程需要在读取前获取 GIL,并在读取后释放它。
    【解决方案2】:

    你有两个选择:

    • 使用-i 标志启动python,这将导致将其放到交互式interperter,而不是退出主线程
    • 手动启动交互式会话:

      import code
      code.interact()
      

      如果你想在它自己的线程中运行交互式会话,第二个选项特别有用,因为某些库(如 PyQt/PySide)不喜欢它不是从主线程启动的:

      from code import interact
      from threading import Thread
      Thread(target=interact, kwargs={'local': globals()}).start()
      ...  # start some mainloop which will block the main thread
      

      local=globals() 传递给interact 是必要的,这样您就可以访问模块的范围,否则解释器会话将只能访问线程范围的内容。

    【讨论】:

    • 带有 -i 标志没有任何反应。其他示例也不起作用。在交互模式下,控制台也会变得奇怪。有时在我打字之前没有“>>>”之类的东西。如果是这种情况,我什至无法在多行上创建 if 语句。而且我不明白您所说的 mainloop 阻塞主线程是什么意思。主线是什么?我需要循环做什么?打招呼
    • 您是把-i 给了interpeter 还是程序?应该是python -i program.py [other_options]。使用 mainloop 我正在考虑像 pyqt 这样的东西,你必须在主线程中调用 app.exec() ,它会阻塞它。但是您的问题听起来更像是两个线程正在尝试同时从 tty 读取。要对此进行测试,您可以使用 this here 之类的东西从套接字读取命令。
    • 是的可能是对的,我不能同时使用一个资源两次。不过,我不知道出了什么问题。我用一个简单的例子更新了我的问题,我试图在仍然使用控制台的同时开始工作。我遵循了您的第二个选项。当交互式控制台启动时,我只是输入:import pk | pk.makeWindowThread() 并且只要窗口正在运行就不能使用控制台。
    • @mata:根据代码,我想 C++ 代码在持有 GIL 时被阻塞,导致解释被锁定而不是竞争 tty 访问。
    猜你喜欢
    • 2018-04-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-09
    • 1970-01-01
    • 2011-12-23
    • 2019-06-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多