Afinant l’entorn multi thread i multiples interprets de python

September 27, 2007 – 6:57 pm

Seguint el fil del post anterior, voldria fer algunes correccions i afegir més informació. Certament resulta suficientment complex el tema de crear un entorn segur de multiples interprets embebits de python en un codi en C com per seguir-ne parlant.

Del codi anterior, acabat a les tantes de la nit, en puc extreure algunes problemàtiques implicites i que forçosament m'han fet replantejar els usos de les funcions que ens serveix Python per tractar els contextes de threads i de retruc els interprets associats.

Python ens dona dos tipus de funcions, unes que podem anomenar de forma entenedora com a alt nivell, aquestes funcions són les seguents :

  • PyEval_AdquireThread (PyThreadState * tstate): Aquesta primera funció ens permet associar el context del thread actual al context python que passaem com a primer parametre, i ens demana explicitament que el context anterior del thread no sigui NULL
  • PyEval_ReleaseThread (PyThreadState * tstate ) : Aquesta segona funcio serveix per destruir el thread, el context python del thread passara a ser NULL.
  • PyThreadState * tstate PyEval_SaveThread : Retorna el context python actual del thread
  • PyEval_RestoreThread (PyThreadState * tstate ) : Molt semblant a la funció AdquireThread

Totes aquestes funcions de per si tanquen el GIL o el cedeixen en les diferents versions i depenent de si fem un Adquire/Restore o Release.

Per tant un us lògic d'aquestes funcions passaria a un codi semblant al seguent

PyEval_AdquireThread ( tstate )

// fer alguna cosa de python en un context de thread python identificat

// per la variable tstate

PyEval_ReleaseThread ( tstate )

Recordem també que un thread amb context de python esta associat a un interpret, i que un interpret pot tenir n threads que l'accedeixin.

Tal i com ja comentavem en l'article anterior l'us dels interprets ens permetre seperar de forma exhaustiva contextos d'aplicacions python en un sol programa C, diguis mod_python.

Del codi que us presentava en el post anterior, hem va sorgir el dubte de si mod_python era capaç d'executar multiples instancies d'un virtual host associat a un interpret, doncs be, mitjançant aquesta pregunta he apres que realment es aixins i que el codi presentat anteriorment era incorrecte.

El codi correcte de la funcio do_something_python_code corregit seria una cosa com aquesta :

 
void * do_something_python_code (void * id)
{
char command[512];
int id_cast = (int ) id;
PyThreadState *tstate;
 
printf("Thread %d execution n", id_cast);
 
PyEval_AcquireLock();
interpreter[id_cast] = Py_NewInterpreter();
PyThreadState_Swap(NULL);
PyEval_ReleaseLock( );
 
PyEval_AcquireThread(interpreter[id_cast]);
 
// do something
sprintf(command, "print \"(%d %cs)\" %c dir()n�", id_cast, '%', '%');
PyRun_SimpleString(command);
PyRun_SimpleString("import sysn");
PyRun_SimpleString("import timen");
PyRun_SimpleString("time.sleep(3)n");
 
sprintf(command, "print \"(%d %cs)\" %c dir()n�", id_cast, '%', '%');
PyRun_SimpleString(command);
 
Py_EndInterpreter(interpreter[id_cast]);
 
printf("Thread %d ending n", id_cast);
 
pthread_exit((void *) 0);
}
 

El canvi substancial que he fet ha sigut pujar primerament la creació del Interpret a la funció del thread i seguidament utilizar exclusivament la funció PyEval_AdquireThread per personalitzar un thread de context python per a ell, finalment utilizo Py_EndInterpreter per destruir no solament l'interpret crear si no tots els treats associats a ell. Per tant no hem caldra utilizar la funció ReleaseThread.

També haureu notat el canvi en la prova de concurrencia, abans utilizava una crida des de C de la syscall sleep(5), ara el que faig es fer una crida de python del tipus "time.sleep(n)".

Mitjançant aquest canvi volia saber si un cop adquirit el GIL mitjaçant PyEval_AdquireThread si executava una ordre que produis un I/O bloquing aquest quedaria alliberat per a altres interprets. La resposta es si.

No cal anar a cercar molt lluny el perque, tal i com nosaltres varem poder implementar i usar les macros PY_BEGIN_ALLOW_THREADS i PY_END_ALLOW_THREADS, la implementació del modul time , si aquesta estigues escrita com una extencio amb C , també ho podria realitzar.

En cas contrari, si el modul time estigues escrit integrament amb python, el core d'aquest incorpora un sistema d'intercanvi de contextos de threads i alliberació del GIL automatic en cas d'executar-se més de 100 bytecode instructions o bé la predicció d'un llarg I/O bloquing.

Pero be tornant una mica a l'inici del post, parlava de que hi havia dos grups definits de funcions un anomenat d'alt nivell i un altre anomenat de baix nivell, aquest segon grup esta format per funcions del tipus PyThreadSate_Swap i d'altres. Es possible reconvertir l'exemple anterior amb aquestes funcions, pero aixo ja sera un altre dia.

Cal dir pero, i com ja diu el manual de Python, les primeres, les d'alt nivell son una abstracció de l'us de les de mes baix nivell.

Post a Comment

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word