'python'에 해당되는 글 2건

  1. 2011.04.26 C, Python, ctypes 사이의 데이터 타입 매핑
  2. 2009.04.14 python embedding multithread
Programing/Python2011. 4. 26. 16:39
 C 타입 파이썬 타입   ctypes 타입
 char   한 문자  c_char
 wchar_t   유니코드 한 문자   c_wchar 
 char  int / long  c_byte
 unsigned char
 (책에는 char로 되어 있으나
  ctypes가 c_ubyte인 것으로
  보아 unsigned char로 추정) 
 int / long  c_ubyte
 short  int / long  c_short
 unsigned short  int / long  c_ushort
 int  int / long  c_int
 unsigned int  int / long   c_uint
 long  int / long   c_long
 unsigned long  int / long   c_ulong
 long long  int / long   c_longlong 
 unsigned long long   int / long   c_ulonglong 
 float   float   c_float 
 double   float   c_double 
 char * (NULL terminated)   string 또는 none   c_char_p 
 wchar_t * (NULL terminated)   unicode 또는 none   c_wchar_p 
 void *   int / long 또는 none   c_void_p 

출처 : 파이썬 해킹 프로그래밍
Posted by ratiel
Programing/Python2009. 4. 14. 17:34


모듈은 import하는데 함수를 못 읽어들이는 상황이 발생
한동안 삽질하다가 오늘 그냥 지금까지 한거 싹 날려버리고 처음부터 차근차근 하니까 성공..
아무래도 python 코드 또는 c 코드에서 잘못된것 같은데..
다음부터 python 코드를 짜면 먼저 작동하는지부터 확인해야겠네요

우선 전체 코드는 다음과 같습니다

#include <Python.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <Windows.h>
#include <process.h>
#define sleep(x) Sleep(1000 * x)
void ThreadProc(void*);
void ThreadProc2(void*);
#define NUM_ARGUMENTS 5
typedef struct
{
 int argc;
 char *argv[NUM_ARGUMENTS];
} CMD_LINE_STRUCT;
int main(int argc, char *argv[])
{
 int i;
 CMD_LINE_STRUCT cmd;
 PyThreadState *mainThreadState;
 PyGILState_STATE gilState;
 HANDLE hThread;
 DWORD dwThreadID;
 cmd.argc = argc;
    for( i = 0; i < NUM_ARGUMENTS; i++ )
    {
        cmd.argv[i] = argv[i];
    }
    if (argc < 3)
    {
        fprintf(stderr, "Usage: call python_filename function_name [args]\n");
        return 1;
    }
 // init python interpreter
 Py_Initialize();
 // init thread support
 PyEval_InitThreads();
 // Save a pointer to the main PyThreadState Object
 mainThreadState = PyEval_SaveThread();
 gilState = PyGILState_Ensure();
 hThread = (HANDLE)_beginthreadex(NULL, 0, (unsigned int(_stdcall*)(void*))ThreadProc, &cmd, 0, (unsigned*)&dwThreadID);
 PyGILState_Release(gilState);
 for(i = 0; i < 10; i++)
 {
  printf("this is main thread...\n");
  sleep(1);
 }
 printf("Main Thread waiting for My Thread to complete...\n");
 WaitForSingleObject(hThread,INFINITE);
 printf("Main thread finished gracefully.\n");
 PyEval_RestoreThread(mainThreadState);
 Py_Finalize();
 return 0;
}
void ThreadProc(void *data)
{
 PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *pSysModules;
 PyGILState_STATE gilState;
 CMD_LINE_STRUCT* arg = (CMD_LINE_STRUCT*)data;
 for (int i = 0; i < 15; i++)
 {
  printf("is threadfunc1...\n");
  sleep(1);
 }
 gilState = PyGILState_Ensure();
 pName = PyString_FromString(arg->argv[1]);
 pModule = PyImport_Import(pName);
 Py_DECREF(pName);
 pDict = PyModule_GetDict(pModule);
 pFunc = PyDict_GetItemString(pDict, arg->argv[2]);
 if(PyCallable_Check(pFunc))
 {
  pValue = PyObject_CallObject(pFunc, NULL);
 }
 else
 {
  PyErr_Print();
 }
 pSysModules = PySys_GetObject("modules");
 if(NULL != pSysModules)
 {
  PyObject *pModuleName = PyString_FromString(arg->argv[1]);
  int ret = PyDict_DelItem(pSysModules, pModuleName);
  if(-1 == ret)
  {
   PyErr_Print();
  }
 }
 Py_DECREF(pModule);
 PyGILState_Release(gilState);
}

테스트용 코드라 변수하고 함수 구분도 그냥 숫자로..ㅋ

설명을 간단하게 하자면

 PyThreadState *mainThreadState;
 PyGILState_STATE gilState;

이 부분에서 PyThreadState와 PyGILState_STATE변수를 정해줍니다.
PyThreadState는 메인스레드의 포인터를 저장할 용도로 정의되고
PyGILState_STATE는 global interpreter lock(이하 GIL)을 잡고, 풀어주는 용도입니다.

 Py_Initialize();
 PyEval_InitThreads();
 mainThreadState = PyEval_SaveThread();
 gilState = PyGILState_Ensure();

python api들의 사용을 위해 Py_Initialize()를 호출하고
PyEval_InitThreads()로 멀티스레드 환경을 가능하게 합니다
PyEval_SaveThread()를 사용하여 메인스레드의 포인터를 변수에 저장하고
PyGILState_Ensure()를 이용해서 GIL를 잡아줍니다.
PyGILState_Ensure()는 python 2.3 이후 버전부터 사용 가능한 api인데요

2.3 이전 버전에서는
PyEval_AcquireLock()을 이용해 GIL를 잡고,
PyThreadState_Swap(PyThreadState*)으로 PyThreadState 객체를 로드한 뒤
파이썬 코드를 실행하고
PyThreadState_Swap(NULL)을 이용, PyThreadState 객체를 언로드하고
PyEval_ReleaseLock()으로 GIL를 풀어주는 작업을 진행했어야 하는데

2.3이후 버전부터는
PyGILState_Ensure() 이 함수 하나만으로
PyEval_AcquireLock()과 PyThreadState_Swap(PyThreadState*)
이 두개의 함수를 사용한 결과와 똑같이 됩니다.

자, GIL까지 잡고.. 그 다음은 c에서 스레드를 만들어서 동작시키고 GIL를 풀어줍니다

PyGILState_Release(gilState);

PyGILState_STATE 가 저장된 변수인 gilState를 인자로하여 PyGILState_Release()를 호출, GIL을 풀어줍니다.
이 역시 위에서 설명한 PyThreadState_Swap(NULL), PyEval_ReleaseLock() 두개의 함수와 동일한 역할을 합니다.
나머진 c 코드니까 넘어가구요,

 PyEval_RestoreThread(mainThreadState);
 Py_Finalize();

이제 프로그램이 끝나니 저장해두었던 mainThreadState가 필요 없어졌으므로  PyEval_RestoreThread()로 저장한것을 없애구요,
더 이상 파이썬 인터프리터를 사용하지 않을것이므로 Py_Finalize()를 호출하여 파이썬 인터프리터를 종료합니다.

이제 스레드 부분을 보시면
 PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *pSysModules;
 PyGILState_STATE gilState;

필요한 PyObject 변수들을 선언하고, 스레드에서도 GIL을 잡고 풀어줘야 하기 때문에 PyGILState_STATE 변수를 선언합니다.

gilState = PyGILState_Ensure();

pName = PyString_FromString(arg->argv[1]);
pModule = PyImport_Import(pName);
Py_DECREF(pName);

pDict = PyModule_GetDict(pModule);
pFunc = PyDict_GetItemString(pDict, arg->argv[2]);

메인 스레드에서와 마찬가지로 PyGILState_Ensure()를 이용, GIL을 잡아줍니다.
그 밑까지는 모듈을 import하고 모듈의 함수를 불러오기까지의 루틴입니다.
모듈을 불러온 뒤 pName은 필요 없어졌으므로 레퍼런스 카운트를 감소시켜 줍니다

if(PyCallable_Check(pFunc))
{
    pValue = PyObject_CallObject(pFunc, NULL);
}
else
{
    PyErr_Print();
}

pSysModules = PySys_GetObject("modules");
if(NULL != pSysModules)
{
    PyObject *pModuleName = PyString_FromString(arg->argv[3]);
    int ret = PyDict_DelItem(pSysModules, pModuleName);
    if(-1 == ret)
    {
       PyErr_Print();
    }
}

Py_DECREF(pModule);
PyGILState_Release(gilState);

이 부분은 함수가 정확하게 로드되었는지 확인 후 로드하고
캐싱된 모듈을 지워주는 부분입니다.
역시 마지막엔 레퍼런스 카운트를 감소시키구요,
GIL을 잡았었으니 PyGILState_Release(gilState)를 이용해 풀어줍니다.


Posted by ratiel