본문 바로가기
개발/Server

[C언어] 쓰레드의 사용

by 열야 2012. 8. 28.

쓰레드는 사용하기 가장 까다로운 녀석이다.

대부분의 어설픈 개발자들이 무턱대고 쓰다가 프로젝트가 끝나지 못하는 경험을 하게 되며, 그렇지 않더라도 자신의 잘못을 모르고 어설프게 쓰래드를 쓰고 있을 것 같다.


여기서 이야기 하고자 하는 것은 매우 간단한 몇가지만 짚고 넘어간다. 실수하기 쉬운 내용들...


쓰레드의 시작

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

쓰레드는 이 함수로부터 시작된다. 그리고 쓰레드가 죽는 원인은 다음 3가지로 정의할 수 있다.

  1. 쓰레드 자신이 pthread_exit를 호출한 경우: 해당 함수 인자를 종료 값으로 pthread_join()을 통해 넘겨준다.
  2. start_routine에서 리턴한 경우: main()함수가 리턴한 것과 같은 효과인데 이 경우는 쓰레드가 종료되며, 1번과 동일하게 pthread_join()에 의해서 결과값을 전달받을 수 있다.
  3. 취소가 된 경우: pthread_cancel
하지만 머니머니 해도 메인쓰래드가 죽으면 다 죽는다.



여기에서 신경써야 하는 부분은 start_routine와 arg이다. 다음 코드는 정상작동하지 않는다.


static int threadStart(int socket)
{
    pthread_t thread_id;
	
    if( pthread_create(&thread_id, NULL, threadMain, (void*) &socket) != 0 )
    {
        int errsv = errno;
        ERROR_SYSTEM();
		ERROR_ASSERT(FALSE);
		return errsv;
    }
    
    return E_OK;
}

static void *threadMain(void *_socket)
{
    int socket = (int) *((int *)_socket);


이 경우 정상작동하지 않는 이유는 다음과 같다.

pthread_create()를 호출할 때 전달한 socket이 문제가 된다. socket은 함수의 인자이므로 자동변수(로컬변수)이며, 함수가 종료되면 자동 해제된다.

그런데, threadMain()함수의 시작 시점은 공식적으로 전혀 알수가 없다. 다시말해, threadStart함수가 종료된 후 호출될 수 있다. 그런 경우, 다른 프로세스 과정 또는 다른 쓰레드에 의해서 해당 영역(스텍영역)이 침범당해서 전혀 다른 값으로 바뀌게 된다.

이를 방지하기 위한 해결책은 쓰레드에 전달하는 인자를 malloc으로 할당하여 heap영역에 저장한 후, theadMain에서 이를 해제 하는 방식이다.


typedef struct {
	int socket;
}socket_pack_t;

static int threadStart(int socket)
{
    pthread_t thread_id;
	socket_pack_t *thread_param = (socket_pack_t *) malloc(sizeof(socket_pack_t));
	memset(thread_param, '\0', sizeof(socket_pack_t));
	thread_param->socket = socket;

    if( pthread_create(&thread_id, NULL, threadMain, thread_param) != 0 )
    {
        int errsv = errno;
        ERROR_SYSTEM();
		ERROR_ASSERT(FALSE);
		return errsv;
    }
    
    return E_OK;
}

static void *threadMain(void *_socket)
{
	socket_pack_t *param = (socket_pack_t *)_socket;
    int socket =  param->socket;
	
	free(param);	//	해제 한다.



이와 같이 하면, 쓰레드의 파라메터가 heap영역에 할당된 메모리에 저장되며, thread에 전달된 후에, 해제될 수 있다.

'개발 > Server' 카테고리의 다른 글

[C언어] 좀비 프로세스를 없애는 더블 fork()..  (3) 2012.08.28