본문 바로가기

Enginius/Matlab

Use Thread in Matlab by mex-compile C code

매트랩에서는 Thread를 지원하지 않는다.

그래서 C에서 Thread code를 작성한 후에 이를 mex compile해서 사용하고자 하는 것이 본 포스팅의 목적이다.

 

#include "mex.h"
#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <string.h>

/* 여기 있는 전역 변수들은 thread와 mexFunction 모두에 있어서 접근이 가능하다.
 * 그렇기 때문에, thread에서 변경하고, matlab에서 읽을 수 있다.
 */
static unsigned Counter;
static HANDLE hThread=NULL;

/* Thread 함수이다. 이 함수를 matlab과 독립적으로 수행되어야만 한다.
 */
unsigned __stdcall SecondThreadFunc( void* pArguments ) {
    /* loop for 20 seconds */
   
    while ( Counter < 20 ) {
        Counter++;
        Sleep( 1000L );
    }
   
    _endthreadex( 0 );
    return 0;
}

/*
 * 이부분이 실제로 mex로 불릴 부분이다.
 */
void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray*prhs[])
{
    unsigned threadID, i;
    char *cmd;
    double *outputDoublePtr, *inputDoublePtr;
   
    /*  Right-hand side Argument의 수를 확인한다.  */
    if(nrhs!=1)
        mexErrMsgTxt("One input required.");
   
    /* Argument는 string 이어야만 한다. (이 예제에선) */
    /*
    if (!mxIsChar(prhs[0]))
        mexErrMsgTxt("Input must be a string.");
     */
    /* Argument는 double 이어야만 한다. (이 예제에선) */
    if (!mxIsDouble(prhs[0]))
        mexErrMsgTxt("Input must be a double.");
   
    /* 해당 argument를 처리할 수 있는 형식으로 읽어온다. */
    // cmd = mxArrayToString(prhs[0]);
    inputDoublePtr = mxGetPr(prhs[0]);
           
    /*
     * cmd에는 우리가 input으로 넣은 text가 들어있다.
     */
    // if (!strcmp(cmd,"Init"))
    if (inputDoublePtr[0] == 0)
    {
        /*
         * 이미 Lock이 걸려 있다는 것은 Thread가 켜졌다는 의미이다.
         * 그렇기 때문에 여기서 에러를 출력한다.
         */
        if (mexIsLocked())
            /* 에러를 출력하고 handle을 matlab으로 옮긴다. */
            mexErrMsgTxt("Thread already initialized.");
       
        /* 우리는 Thread를 키기 때문에 Lock을 거는 것이 중요하다. */
        mexLock();
       
        /* Counter를 초기화 한다.  */
        Counter = 0;
        /* 드디어 Thread를 킨다. */
        mexPrintf( "Creating Thread...\n" );
        hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID );
    }
    // else if (!strcmp(cmd,"test"))
    else if (inputDoublePtr[0] == 1)
    {
        /* 중간에 Thread에서 돌고 있는 Counter를 확인할 수 있게 한다. */
        mexPrintf( "Counter: %d sec \n", Counter );
    }
    else
    {
        /* Lock이 걸려있지 않다는 것은 Thread 역시 켜져있지 않다는 의미이다. */
        if (!mexIsLocked())
            mexErrMsgTxt("Thread not initialized yet."); /*This function will return control to MATLAB*/

        /* Thread가 끝날 때 까지 기다린다. */
        WaitForSingleObject( hThread, INFINITE );
        mexPrintf( "Counter should be 20; it is-> %d\n", Counter );
       
        /* Thread를 종료하고 Unlock한다. */
        CloseHandle( hThread );
        mexUnlock();
    }
   
    /* 출력할 것이 있을 때만 출력을 한다.  */
    if (nlhs == 1)
    {
        plhs[0] = mxCreateDoubleMatrix(1, 10, mxREAL);
        /* Get Pointer for 출력 */
        outputDoublePtr = mxGetPr(plhs[0]);
        for( i = 0; i<10; i++)
            outputDoublePtr[i] = i;
    }
   
    return;
}

 

위의 코드를 수행하는 matlab 코드는 다음과 같다. 

%% 초기화한다.
clc;
clear all;
close all;

 

%% mex compile한다.
mex mex_thread.c

 

%% Thread 킨다.
a = mex_thread(0);
disp(a);

 

%% Thread 테스트
a = mex_thread(1);
disp(a);

 

%% Thread 종료 (초기화 하고 20초 후에 종료)
a = mex_thread(2);
disp(a);