Python游戏服务器开发日记(二)绕过GIL启动多线程Python环境

22 篇文章 0 订阅
14 篇文章 0 订阅

        说道Python和多线程,很容易想到GIL,GIL意味着只要是用Python做的多线程程序,就无法利用多个CPU。

        经过一些失败的尝试后,我也一度认为GIL是无解的。我们甚至把注意力转向了IronPython等无锁Python,但是实际上那样问题可能更多,比如我们不熟悉mono,mono也没达到完全成熟的程度。


        直到skynet的QQ群里一位朋友介绍了另一种加载so的方式,事情才有了180度的变化。


        方法如下:

        1、编译Python源码,编译时加上参数--enable-shared,编译成so动态链接库。

        2、找到so,拷贝N份,分别命名为 libpython_1.so, libpython_2.so, ... 等等

        3、用C语言,使用linux下的ldfcn库,动态加载这些so库,ldfcn可以保证同名全局变量不冲突。

        

        (C语言源码,待整理)

#include <stdio.h>
#include "Python.h"
#include <dlfcn.h>
#include <pthread.h>
 
#define N 5
 
void* h[N] = { NULL };
 
void thread( int index )
{
    void(*py_init)();
    int(*is_init)();
    PyObject*(*py_import)(char*);
    PyObject*(*py_getattr_bystr)(PyObject*, char*);
    PyObject*(*py_call)(PyObject*, PyObject*);
 
    py_init = NULL;
    is_init = NULL;
    py_import = NULL;
    py_getattr_bystr = NULL;
    py_call = NULL;
 
    PyObject* mod = NULL;
    PyObject* func = NULL;
    PyObject* ret = NULL;
 
    py_init = dlsym( h[index], "Py_Initialize" );
    is_init = dlsym( h[index], "Py_IsInitialized" );
    py_import = dlsym( h[index], "PyImport_ImportModule" );
    py_getattr_bystr = dlsym( h[index], "PyObject_GetAttrString" );
    py_call = dlsym( h[index], "PyObject_CallObject" );
 
    py_init();
    mod = py_import( "hello" );
    func = py_getattr_bystr( mod, "output" );
    ret = py_call( func, NULL );
     
    return;
}
 
int main()
{
    char temp[64];
    int i;
    int ret;
    pthread_t tid[5] = {0};
 
    for (i=0; i<N; ++i) {
        sprintf( temp, "libpython/libpython3.4m_%d.so", i+1 );
        //h[i] = dlopen( temp, RTLD_NOW | RTLD_DEEPBIND );
        h[i] = dlopen( temp, RTLD_NOW );
    }
 
    for ( i=0; i<N; ++i ) {
        ret=pthread_create( &tid[i], NULL, (void *)thread, (void*)i ); // 成功返回0,错误返回错误编号
    }
 
    for ( i=0; i<N; ++i ) {
        pthread_join( tid[i],NULL );
    }
 
 
    return 0;
}

        (编译注意事项,待整理)


        (设置环境变量,待整理)


        用简单Py脚本测试,会发现脚本的运行确实是独立的。但是如果用了 import time,则会造成段错误。

        原因是用这种方法,以后import的各种python的库,如果是C语言扩展的,那么就会因为重复而冲突。

        解决方法是在编译Python时,加入--enable-time,把time库和python库编译到一起,就能解决这个问题。(解决方法见后面)

        (另一种备选方案是找到time源码,在库名后加上数字,也编出N份time_1, time_2... 等等,在脚本里也分别import不同的time库,即可。)


        (2015-9-3注:对这个问题的理解在本系列第六章会有深入的解答。)


        由于我们的Python环境禁止在脚本内启动多线程,所以可以加入编译参数--without-threads,这样还能稍微提高一点运行效率。


2015-8-26补充:

        在编译python时把其他module加入同一个so:

        修改 Module/Setup 文件,查找datetime,按照datetime同样的格式写上你要打包的源文件和目标名称。被注释的库取消注释也可以打包。


        检查是否打包成功:

        用vim之类的文本编辑器,直接打开so文件,搜索关键字,比如datetime,即可确认你的源码是否被打包进入so。


(运行的时候注意确认,用python启动的是哪一个so,是系统的还是自己编译的。建议每次启动都临时修改LD_LIBRARY_PATH环境变量,避免污染系统环境。)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值