文档章节

Cython进阶--用Cython封装Callback函数

mickelfeng
 mickelfeng
发布于 2017/07/21 17:17
字数 1366
阅读 62
收藏 1

Cython封装Callback函数

1 说明:

回调函数,在C语言里是经常要用到的,但是,在Python里封装一个C的回调函数并没有想象的那么简单,本文讲解如何用Cython快速的封装C里的回调函数

2 不多说,先上代码:

callback.pyx

cdef extern from "pthread.h":

    ctypedef void * pthread_t

    ctypedef structpthread_attr_t:

        pass

    int pthread_create (pthread_t*__newthread, \

                  pthread_attr_t *__attr, \

                  void *(*__start_routine) (void *) except*, \

                  void *__arg)

    int pthread_join (pthread_t__th, void **__thread_return)

cdef extern from "Python.h":

    ctypedef enum PyGILState_STATE:

        pass

    ctypedef enum PyThreadState:

        pass

    PyGILState_STATE PyGILState_Ensure()

    void PyGILState_Release(PyGILState_STATE)

    PyThreadState *PyEval_SaveThread()

    void PyEval_RestoreThread(PyThreadState *)

    void PyEval_InitThreads()

    void Py_INCREF(object obj)

    void Py_DECREF(object obj)

def Callback(obj):

    if hasattr(obj,'run'):

       obj.run()

cdef void * start (void * param) except*:

    cdef PyGILState_STATE state

    cdef object obj = <object>param

    cdef void *ret

    if not param:

        return <void *>0

    state = PyGILState_Ensure()

    Callback(obj)

    Py_DECREF(obj)

    PyGILState_Release(state)

    return <void*>1

def CreateThread(obj):

    cdef int ret

    cdef pthread_t tid

    PyEval_InitThreads()

    Py_INCREF(obj)

    ret = pthread_create(&tid, <pthread_attr_t *>0,start,<void *>obj)

    if ret != -1:

        return <int>tid

    else:

       Py_DECREF(obj)

        return None

def JoinThread(int tid):

    cdef PyThreadState * state

    cdef void *thread_return =<void *>0

    cdef int ret

    state = PyEval_SaveThread()

    ret =pthread_join(<pthread_t>tid,&thread_return)

    PyEval_RestoreThread(state)

    if ret != -1:

        return <int>thread_return

    else:

        return <int>0

3 解释一下:

3.1 这个例子封装了多线程库pthread的两个函数:

pthread_create

pthread_join

 

3.2 实际上,python基于Linux的多线程的实现正是基于pthread库的

3.3 cdefextern from "pthread.h" 引用了两个函数的定义:pthread_create和pthread_join

3.4 cdefextern from "Python.h" 引用了Python的五个库函数:

 PyEval_InitThreads

PyGILState_Ensure

PyGILState_Release

PyEval_SaveThread

PyEval_RestoreThread

3.5 一个一个来解析一下:

 

3.5.1 PyEval_InitThreads

     如果要让Python代码在不同的线程里调用,必须首先调用一下PyEval_InitThreads,只需调用一次即可,多次调用没有影响,PyEval_InitThreads会判断是否已经作了初始化

 

3.5.2 PyGILState_Ensure和PyGILState_Release

      这里就是Python颇具争议的GIL(global interpreter lock),翻译成中文就是全局解析锁,出现这个东西的原因是Python的内存管理不支持多线程,网上的资源一大堆,可以去查,python发展到现在,GIL一直还存在,是有其理由的,

其一:

        它确实简化了Python的设计,也使Python变得更稳定,而在《The Zen of Python》中,第三条就是:Simple is better than complex,可以说在奉行“大道至简”的设计准则的Python来说,其原因就很好理解了。

其二:

        在单CPU的情况下GIL造成对效率的影响微乎其微,优化Python代码本身的性能更为重要。

其三:

        在多CPU情况下,为了做到并行处理,可以用进程来替代线程,在Python里有一个Process类可以做到在Python里多进程运行

其四:

        好吧,我也认为这个东西让我感到不爽,它在简化设计者任务的同时,也加重了使用者的负担。但是暇不掩玉,就像人一样,如果总叮着一个人的缺点不放,不但会影响自己的心情,还有可能会使自己失去与人交往的机会。

 

3.5.3 PyEval_SaveThread和PyEval_RestoreThread

 

可以说,这两个函数是因GIL而生,由于Python的GIL只允许同时只有一个线程执行Python代码,这就有个问题:如果一个调用C代码的操作比较 耗时,比I/O操作,那么其它线程就会被阻塞,更甚者假如一个Python线程在等另一个Python线程,那么就会造成死锁:PyEval_SaveThread的作用就是暂时释放GIL,使其它线程里的Python代码得以有机会运行

3.6 实现的原理是这样的:

在pthread_create时将一个对象作为线程的参数传递下来

在start线程中,再调用对象的方法run,就达到了在Callback中调用Python代码的目的

为了防止通过CreateThread传递进来的对象obj在start还没有运行或者没有运行完就被垃圾回收,在pthread_create之前还调用了Py_INCREF将obj引用计数加一

在start里,运行完Python代码后,再调用Py_DECREF将obj的引用计数减一

 

4 再来看下Python代码是怎么使用这些接口的:

callme.py

callme.py

#!/usr/bin/python

import callback

import time

class Thread:

    def __init__(self):

        self.tid= None

    def start(self):

        self.tid= callback.CreateThread(self)

    def run(self):

        pass

    def join(self):

       callback.JoinThread(self.tid)

class MyThread1(Thread):

    def run(self):

        for i in xrange(10):

            print 'outputfrom thread 1:',i

class MyThread2(Thread):

    def run(self):

        for i in xrange(10):

            print 'outputfrom thread 2:',i

t1 = MyThread1()

t2 = MyThread2()

t1.start()

t2.start()

t1.join()

t2.join()

基本上,这个例子就是一个Python内部Thread实现的精简版

除了CreateThread时传递了一个对象外,其它的没有什么好解释的了

5 最后来看下编译的代码和运行结果

Setup.py

Setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

ext = Extension("callback", 
                   define_macros = [('MAJOR_VERSION', '1'),
                                    ('MINOR_VERSION', '0')],
                   include_dirs = ['/usr/local/include'],
                   libraries = ['pthread'],
                   library_dirs = ['/usr/local/lib'],
                   sources = ["callback.pyx", ])
setup(
    name = 'callback',
    version = '1.0',
    description = 'This is a callback demo package',
    author = '',
    author_email = 'shi19@163.com',
    url = '',
    long_description = '',
    ext_modules=cythonize([ext,]),
)

5.1 运行python Setup.py build_ext --inplace进行编译

5.2 编译完了,调用

python callme.py

查看结果:

output from thread 1:output from thread 2:0
output from thread 2: 1
output from thread 2: 2
output from thread 2: 3
output from thread 2: 4
 output from thread 2: 0
5
output from thread 1: 1
output from thread 1: 2
output from thread 2: 6
output from thread 1: 3
output from thread 2: 7
output from thread 1: 4
output from thread 1: 5
output from thread 2: 8
output from thread 1: 6
output from thread 2: 9
output from thread 1: 7
output from thread 1: 8
output from thread 1: 9

不要认为是输出有问题,这正好说明是在多线程的情况下运行的。

示例代码下载

(完)

本文转载自:http://blog.csdn.net/i2cbus/article/details/18415333

mickelfeng

mickelfeng

粉丝 237
博文 2784
码字总数 604219
作品 0
成都
高级程序员
私信 提问
Speed-up with Cython and Numpy in Python

Cython代码和Python代码区别 代码运行在IPython-Notebook中,在IPython-Notebook中导入cython环境。 1 %load_ext cython Cython可以在Python中掺杂C和C++的静态类型,cython编译器可以把Cyt...

Arthurcsh
2017/10/13
0
0
比Python快100倍,利用Cython实现高速NLP项目

选自Medium,作者:Thomas Wolf,机器之心编译。 相关 Jupyter Notebook 地址:github.com/huggingface… 去年我们发布 Python 包 coreference resolution package 后,我们收到了来自社区的...

2018/07/16
0
0
教程 | 比Python快100倍,利用spaCy和Cython实现高速NLP项目

  选自Medium   作者:Thomas Wolf   机器之心编译   参与:乾树、刘晓坤      Cython 是一个工具包,可以使你在 Python 中编译 C 语言,这就是为什么 numpy 和 pandas 很快的原...

机器之心
2018/07/15
0
0
Cython 三分钟入门

作者:perrygeo 译者:赖勇浩(http://laiyonghao.com) 原文:http://www.perrygeo.net/wordpress/?p=116 我最喜欢的是Python,它的代码优雅而实用,可惜纯粹从速度上来看它比大多数语言都要...

鉴客
2012/02/23
9.8K
4
Python 性能优化

原文出处:xybaby 本文除非特殊指明,”python“都是代表CPython,即C语言实现的标准python,且本文所讨论的是版本为2.7的CPython。另外,本文会不定期更新,如果大家有一些好的想法,请在评...

xybaby
2017/11/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Blockstack-2 :Blockstack ID注册

本篇文章主要记录Blockstack ID注册的流程; 在介绍注册流程之前,先简单的介绍一下Blockstack ID; 相对于传统互联网来说,Blockstack ID更像是统一的账号系统;即一个账号即可登录和授权所...

Riverzhou
45分钟前
11
0
面试官问:平时碰到系统CPU飙高和频繁GC,你会怎么排查?

处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及Full GC次数过多的问题。当然,这些问题的最终导致的直观现象就是系统运行缓慢,并且有大量的报警。本文主要针对系统运...

Java高级架构师n
今天
24
0
面向对象编程

1、类和对象 类是对象的蓝图和模板,而对象是实例;即对象是具体的实例,类是一个抽象的模板 当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定...

huijue
今天
25
0
redis异常解决 :idea启动本地redis出现 jedis.exceptions.JedisDataException: NOAUTH Authentication required

第一次安装在本地redis服务,试试跑项目,结果却出现nested exception is redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required错误,真是让人头疼 先检查一...

青慕
今天
35
0
Spring 之 IoC 源码分析 (基于注解方式)

一、 IoC 理论 IoC 全称为 Inversion of Control,翻译为 “控制反转”,它还有一个别名为 DI(Dependency Injection),即依赖注入。 二、IoC方式 Spring为IoC提供了2种方式,一种是基于xml...

星爵22
今天
36
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部