日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2024/1/17 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Cython进阶--用Cython封装Callback函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。