不少時候,須要把控制權限交給用戶,或者在擴展裏完成某件過後去回調用戶的方法。php
在PHP擴展裏是經過 call_user_function_ex
函數來調用用戶空間的函數的。swoole
它的定義在 Zend/zend_API.h
:多線程
#define call_user_function_ex(function_table, object, function_name, retval_ptr, param_count, params, no_separation, symbol_table) _call_user_function_ex(object, function_name, retval_ptr, param_count, params, no_separation)
經過宏定義替換爲_call_user_function_ex
,其中參數 function_table
被移除了,它之因此在API才存在大概是爲了兼容之前的寫法。函數的真正定義是:併發
ZEND_API int _call_user_function_ex( zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[], int no_separation);
參數分析:異步
zval *object
:這個是用來咱們調用類裏的某個方法的對象。函數
zval *function_name
:要調用的函數的名字。測試
zval *retval_ptr
:收集回調函數的返回值。優化
uint32_t param_count
:回調函數須要傳遞參數的個數。ui
zval params[]
: 參數列表。spa
int no_separation
:是否對zval進行分離,若是設爲1則直接會出錯,分離的做用是爲了優化空間。
PHP_FUNCTION(hello_callback) { zval *function_name; zval retval; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &function_name) == FAILURE) { return; } if (Z_TYPE_P(function_name) != IS_STRING) { php_printf("Function require string argumnets!"); return; } //TSRMLS_FETCH(); if (call_user_function_ex(EG(function_table), NULL, function_name, &retval, 0, NULL, 0, NULL TSRMLS_CC) != SUCCESS) { php_printf("Function call failed!"); return; } *return_value = retval; zval_copy_ctor(return_value); zval_ptr_dtor(&retval); }
zval_copy_ctor()
原始(zval)的內容拷貝給它。zval_ptr_dtor()
釋放空間。return_value
不是一個函數外的變量,它的由函數聲明裏的變量。PHP_FUNCTION(hello_callback)
這個聲明是簡寫,最終會被預處理宏替換爲:
void zif_hello_callback(zend_execute_data *execute_data, zval *return_value)
return_value
變量其實也就是最終返回給調用腳本的,RETURN_STR(s)
等返回函數最終也都是宏替換爲對該變量的操做。
測試腳本:
<?php function fun1() { for ($i = 0; $i < 5; $i++) { echo 'fun1:'.$i."\n"; } return 'call end'; } echo hello_callback('fun1');
早期的php不支持多進程多線程的,如今隨着發展有不少擴展不斷完善它,諸如pthread
,swoole
等,不只能多線程,並且能實現異步。
利用c語言多線程pthread庫來實現一個簡單的並行擴展。
先聲明咱們一會用到的結構:
struct myarg { zval *fun; zval ret; };
線程函數:
static void my_thread(struct myarg *arg) { zval *fun = arg->fun; zval ret = arg->ret; if (call_user_function_ex(EG(function_table), NULL, fun, &ret, 0, NULL, 0, NULL TSRMLS_CC) != SUCCESS) { return; } }
函數的實現:
PHP_FUNCTION(hello_thread) { pthread_t tid; zval *fun1, *fun2; zval ret1, ret2; struct myarg arg; int ret; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &fun1, &fun2) == FAILURE) { return; } arg.fun = fun1; arg.ret = ret1; ret = pthread_create(&tid, NULL, (void*)my_thread, (void*)&arg); if(ret != 0) { php_printf("Thread Create Error\n"); exit(0); } if (call_user_function_ex(EG(function_table), NULL, fun2, &ret2, 0, NULL, 0, NULL TSRMLS_CC) != SUCCESS) { return; } pthread_join(tid, NULL); RETURN_NULL(); }
測試腳本:
<?php function fun1() { for ($i = 0; $i < 5; $i++) { echo 'fun1:'.$i.'\n'; } } function fun2() { for ($i = 0; $i < 5; $i++) { echo 'fun2:'.$i.'\n'; } } hello_thread('fun1', 'fun2'); echo 'after 多併發';
輸出:
兩次的輸出結果不同,而且echo 'after 多併發';
是在兩個函數都運行完後才執行的。