V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
zhiqiang
V2EX  ›  Python

C++和 Python 混合编程时 Python 创建的子线程不会运行的问题

  •  1
     
  •   zhiqiang · 2017-08-17 17:28:27 +08:00 · 4269 次点击
    这是一个创建于 2658 天前的主题,其中的信息可能已经有所发展或是发生改变。

    假设一个 Python 程序,内部有一个子线程。那么 C++调用该程序时,子线程不会自动运行。只有 C++在调用 Python 程序时,该子线程才会随之运行一小段时间。

    示例 Python 程序 test_python_thread.py如下,调用的 c++代码test_python_thread.cpp附在后面。编译后直接运行./test_python_thread无输出,输入xxx后输出十个数字后停止。但按道理,python 代码应该会每秒钟输出一个数字。

    我想知道怎么才能让 Python 里面的子线程能够不间断运行(每秒钟输出 1 个数字)?

    #!/usr/bin/env python
    # encoding: utf-8
    
    import thread, time
    
    class PyTest:
        def __init__(self):
            def local_python_thread(s):
                while True:
                    print "local_python_thread", s
                    s += 1
                    time.sleep(1)
            thread.start_new_thread(local_python_thread, (0, ))
    
        def xxx(self):
            s = 0
            while s < 10:
                print "cpp_thread", s
                s += 1
                time.sleep(1)
    

    示例 C++程序 (test_python_thread.cpp

    #include <python2.7/Python.h>
    #include <boost/python.hpp>
    #include <boost/filesystem.hpp>
    #include <iostream>
    #include <thread>
    
    
    namespace bp = boost::python;
    
    class CppTest
    {
    private:
        bp::object python_module;
        bp::object python_class;
        bp::object python_object;
        bp::object python_xxx;
    
    public:
        CppTest()
        {
            Py_Initialize();    // 初始化
    
            PyRun_SimpleString("import sys");
            boost::filesystem::path path = "./test_python_thread.py";
            std::string chdir_cmd = std::string("sys.path.append(\"") + path.parent_path().c_str() + "\")";
            PyRun_SimpleString("sys.path.append('./')");
            PyRun_SimpleString(chdir_cmd.c_str());
    
            python_module = bp::import(path.stem().c_str());
            python_class = python_module.attr("PyTest");
    
            python_object = python_class();
    
            python_xxx = python_object.attr("xxx");
        }
    
        virtual void xxx()
        {
                python_xxx();
        }
    };
    
    int main()
    {
        CppTest test{};
    
        std::string cmd;
        while (true) {
            std::getline(std::cin, cmd);
            if (cmd == "xxx") {
                test.xxx();
            }
        }
    
        return 0;
    }
    

    编译 cpp(需要 boost 库):

    g++ -std=c++0x -o test_python_thread  test_python_thread.cpp -lboost_system -l boost_filesystem -l python2.7 -g -I /usr/include/python2.7/ -l boost_python 
    
    13 条回复    2017-08-18 08:43:43 +08:00
    hl
        1
    hl  
       2017-08-17 18:27:44 +08:00
    每太看懂啥意思,不知道是不是因为没有 join 线程的问题导致的.
    albertofwb
        2
    albertofwb  
       2017-08-17 18:38:44 +08:00 via Android
    主进程退出之后,其所创建的所有子线程也会退出。python 创建子线程的时候,有一个属性里设置好之后,可以防止这个。好像是 SetDaemon(True) 这个属性。
    dbow
        3
    dbow  
       2017-08-17 18:49:12 +08:00
    主线程退出 ,daemon thread 也会退出 , 换成下面的方式试试。
    t = threading.Thread(target=local_python_thread, args=(0, ))
    t.start()
    ->
    daemon
    A boolean value indicating whether this thread is a daemon thread (True) or not (False). This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False.
    takeoffyoung
        4
    takeoffyoung  
       2017-08-17 18:57:52 +08:00
    thread 已经是 deprecated 了
    推荐的做法是,创建一个继承自 threading.Thread 的类的实例 obj.(需要实现 run()函数)
    然后 obj.setDaemon(True); obj.start();
    while threading.active() > 0:
    pass
    保证主进程不退出,后台线程得以正确运行
    wangxn
        5
    wangxn  
       2017-08-17 19:39:09 +08:00 via Android
    那个创建的线程直接被回收了吧……连引用都没。
    zhiqiang
        6
    zhiqiang  
    OP
       2017-08-17 20:39:51 +08:00 via Android
    @albertofwb @dbow 主进程没有退出,一直在 getline 等待用户输入。
    zhiqiang
        7
    zhiqiang  
    OP
       2017-08-17 20:42:05 +08:00 via Android
    那个线程一直还在,只是没有运行。

    每次输入 xxx 后会该线程会运行输出 10 个数。
    zhouheyang0919
        8
    zhouheyang0919  
       2017-08-17 20:46:04 +08:00   ❤️ 1
    @zhiqiang

    C++ 代码没有调用 Python 解释器时,应该显式释放 GIL 来允许 Python 代码执行。

    调用进 Python 代码前,应重新锁定 GIL。
    zhouheyang0919
        9
    zhouheyang0919  
       2017-08-17 20:48:06 +08:00
    @zhiqiang

    关键词 PyGILState
    ysc3839
        10
    ysc3839  
       2017-08-17 22:12:46 +08:00
    @zhouheyang0919
    这里有个说明:
    https://docs.python.org/3/c-api/init.html#releasing-the-gil-from-extension-code
    大概是说,你的程序在执行耗时操作,同时不调用 Python 的话这样写代码:
    Py_BEGIN_ALLOW_THREADS
    ... Do some blocking I/O operation ...
    Py_END_ALLOW_THREADS
    可以让 Python 的线程并行运行。
    ysc3839
        11
    ysc3839  
       2017-08-17 22:14:54 +08:00
    zhiqiang
        12
    zhiqiang  
    OP
       2017-08-18 08:22:41 +08:00
    谢谢 @ysc3839 @zhouheyang0919 点到位了。我面临的情况比示例要复杂,C++和 Python 里都有多线程,而且互相调用。我先去尝试一下,有问题再请教。
    ysc3839
        13
    ysc3839  
       2017-08-18 08:43:43 +08:00 via Android
    @zhiqiang 前面 Python Docs 里也有说 C++ 多线程该怎么做。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5477 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 08:36 · PVG 16:36 · LAX 00:36 · JFK 03:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.