2019独角兽企业重金招聘Python工程师标准>>>

Cython封装Callback函数

1 说明:

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

2 不多说,先上代码:

callback.pyx

cdef extern from "pthread.h":ctypedef void * pthread_tctypedef structpthread_attr_t:passint 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:passctypedef enum PyThreadState:passPyGILState_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 statecdef object obj = <object>paramcdef void *retif not param:return <void *>0state = PyGILState_Ensure()Callback(obj)Py_DECREF(obj)PyGILState_Release(state)return <void*>1def CreateThread(obj):cdef int retcdef pthread_t tidPyEval_InitThreads()Py_INCREF(obj)ret = pthread_create(&tid, <pthread_attr_t *>0,start,<void *>obj)if ret != -1:return <int>tidelse:Py_DECREF(obj)return Nonedef JoinThread(int tid):cdef PyThreadState * statecdef void *thread_return =<void *>0cdef int retstate = PyEval_SaveThread()ret =pthread_join(<pthread_t>tid,&thread_return)PyEval_RestoreThread(state)if ret != -1:return <int>thread_returnelse: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/pythonimport callbackimport timeclass Thread:def __init__(self):self.tid= Nonedef start(self):self.tid= callback.CreateThread(self)def run(self):passdef join(self):callback.JoinThread(self.tid)class MyThread1(Thread):def run(self):for i in xrange(10):print 'outputfrom thread 1:',iclass MyThread2(Thread):def run(self):for i in xrange(10):print 'outputfrom thread 2:',it1 = 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 cythonizeext = 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: 4output 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

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

示例代码下载

(完)

转载于:https://my.oschina.net/mickelfeng/blog/1477278

Cython进阶--用Cython封装Callback函数相关推荐

  1. 封装Ajax请求,callback函数参数分析

    /** * 封装Ajax函数 * 功能:发送一个Ajax请求,并且可以执行回调函数 * @param url *            发送请求的地址 * @param params *       ...

  2. 前端基础进阶(七):函数与函数式编程

    纵观JavaScript中所有必须需要掌握的重点知识中,函数是我们在初学的时候最容易忽视的一个知识点.在学习的过程中,可能会有很多人.很多文章告诉你面向对象很重要,原型很重要,可是却很少有人告诉你,面 ...

  3. javascript callback函数的理解与使用

    最近做的一个项目中用到了callback函数,于是就研究了下总结下我对javascript callback的理解 首先从callback的字面翻译"回调" 可以理解这是一个函数被 ...

  4. 封装CopyFileEx函数,实现文件复制中的暂停,控速,获取进度。

    封装CopyFileEx函数,实现文件复制中的暂停,控速,获取进度等. 我的第一篇博客 前段时间无意间想到如何控制文件复制过程的复制速度,并且能实时获得复制进度.对于一个几兆甚至更小的文件,调用API ...

  5. ajax背景、ajax对象、ajax状态、ajax与http、ajax请求数据接口、同步与异步、ajax请求XML数据、封装ajax函数、artTemplate简介、同源策略和跨域请求、JSONP

    AJAX简介: ajax背景: 1.AJAX(Asynchronous JavaScript And Xml)异步的 JavaScript 和 XML:ajax是浏览器提供的一套API,最早出现在谷歌 ...

  6. callback函数_Nodejs 源码解析 util.promisify 如何将 Callback 转为 Promise

    Nodejs util 模块提供了很多工具函数.为了解决回调地狱问题,Nodejs v8.0.0 提供了 promisify 方法可以将 Callback 转为 Promise 对象. 工作中对于一些 ...

  7. 静态成员函数运用在CALLBACK函数和线程函数中《转载》

    CALLBACK函数要声明称 static或全局,而在静态的函数中药调用非静态成员还不能调用,必须把这些成员也设置成静态的,不怎么方便,有好的解决方法吗. 其原因是把CALLBACK函数封装成C++类 ...

  8. 封装CopyFileEx函数,实现文件复制中的暂停,控速,获取进度

    封装CopyFileEx函数,实现文件复制中的暂停,控速,获取进度等. 前段时间无意间想到如何控制文件复制过程的复制速度,并且能实时获得复制进度.对于一个几兆甚至更小的文件,调用API函数CopyFi ...

  9. itwangyang - 再次继续卷起来-20 个 JS 封装工具函数

    itwangyang - 再次继续卷起来-20 个 JS 封装工具函数 前言 日常开发中,面对各种不同的需求,我们经常会用到以前开发过的一些工具函数,把这些工具函数收集起来,将大大提高我们的开发效率. ...

最新文章

  1. android源码中常用的Rect方法
  2. Windows中使用Python和C/C++联合开发应用程序起步
  3. 通过History Trends Unlimited通过统计服务器上Chrome浏览器Top10网页历史访问量(2021.11.23)
  4. python中os.listdir()的使用和文件批量重命名方法
  5. less中each的用法
  6. 轨迹相似性度量之基于Hausdorff与LCSS的理解
  7. MyEclipse中更改JRE环境
  8. android 开发种子文件,IT之家学院:如何制作种子文件和磁力链接
  9. 优麒麟使用教程第四期:Linux平台U盘启动盘制作(建议收藏)
  10. Python利用bs4批量抓取网页图片并下载保存至本地
  11. linux 向终端 发送消息,Linux向不同的连接终端窗口发送消息
  12. Android 9.0 行为变更(一)针对所有 API 级别的应用
  13. sklearn_逻辑回归制作评分卡_菜菜视频学习笔记
  14. 【Unity Shader编程】之十四 边缘发光Shader Rim Shader 的两种实现形态
  15. 马化腾曾在家搞四条电话线和8台电脑,做慧多网深圳站站长
  16. 前世五百次的回眸,才换来今生的擦肩而过。
  17. ThinkPad开机进入启动项选择、Bios以及更改Fn功能键、交换Fn和Ctrl位置
  18. CF528D Fuzzy Search FFT
  19. [文字]史上最NB的绕口令~
  20. 分享99个ASP新闻文章源码,总有一款适合您

热门文章

  1. python中表示单一数据的类型被称为_各种Python数据类型的完整列表
  2. gltexsubimage2d 性能_风水轮流转!AMD R5 5600X单核性能碾压十代i9
  3. php开发领域,PHP-MySQL相关领域
  4. sql数据迁移到oracle数据库,从Oracle到SQL Server数据库主键的迁移
  5. mac mysql-python 失败_MAC OS安装MySQL-python总是失败,请帮忙看看什么原因?
  6. java计算并显示学生的成绩_Java开学测试-学生成绩管理系统
  7. 22考生这些院校计算机专业改考408
  8. jrebel不能使用ajax,Jrebel不生效的原因和解决办法
  9. JS判断 浏览器 是否禁用Cookie
  10. ES6基础4(数据结构)-学习笔记