Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
05-05 01:06
관리 메뉴

nomad-programmer

[Programming/C] 스레드 (Thread) 본문

Programming/C

[Programming/C] 스레드 (Thread)

scii 2020. 6. 20. 23:02

프로세스가 언제나 답이 되지는 못한다. 다음과 같은 이유가 있기 때문이다.

프로세스를 생성하려면 시간이 걸린다.

어떤 컴퓨터는 프로세스를 새로 만들려면 시간이 꽤 오래 걸린다. 아주 오랜 시간은 아니지만 약간의 시간이 걸린다. 추가로 수행할 일이 0.02~0.03초 걸리더라도 매번 프로세스를 새로 만드는 건 그리 효율적이지 않다.

프로세스는 데이터를 공유하기가 까다롭다.

자식 프로세스를 만들면 부모 프로세스의 모든 데이터를 완전히 복사하게 된다.
그러나 데이터를 복사했으므로, 자식 프로세스가 부모 프로세스에 데이터를 다시 보내려면 파이프와 같은 메커니즘을 사용해야 한다.

프로세스는 그저 어렵기만 하다.

프로세스를 생성하려면 많은 코드를 짜야 한다. 그러면 프로그램은 길어지고, 지저분해지기 마련이다.

 

스레드 사용

멀티스레드 프로그램은 가게에서 여러 사람이 일하는 것과 같다. 한 사람이 계산대에서 일하면, 다른 사람은 선반을 채우고, 또 다른 사람은 다른 일을 한다.
한 가게에 여러 명이 일하는 것과 마찬가지로, 한 프로세스 안에 여러 스레드가 일하게 할 수 있다. 스레드들은 모두 똑같은 힙 메모리에 접근할 수 있다. 모두 동일한 파일을 읽고 쓸 수 있고 똑같은 네트워크 소켓에 이야기할 수 있다. 한 스레드가 전역 변수의 값을 바꾸면 다른 스레드는 바뀐 값을 바로 볼 수 있다.
따라서 각 스레드에 별도의 일을 맡기고 한꺼번에 모든 일을 할 수 있게 된다.

모든 스레드가 한 프로세스 안에서 실행될 수 있다.

스레드 라이브러리는 몇 가지 있다. 그 중 가장 인기 있는 것은 "포직스 스레드 라이브러리" 이다. 간단히 "pthread" 라고도 한다.
pthread 라이브러리는 시그윈, 리눅스, 맥에서 모두 사용할 수 있다.

// 스레드 함수는 반환형이 void* 형이어야 한다.
void* test(void * a);
스레드 함수의 반환형은 반드시 void* 형이어야 한다.

 

pthread_create() 함수로 스레드를 생성할 수 있다.

#include <pthread.h>

// 스레드에 대한 모든 정보를 저장
pthread_t t0;
// 스레드 생성
int ste = pthread_create(&t0, NULL, test, NULL);
if (ste == -1) {
    puts("스레드 생성 실패");
}

위의 코드를 실행하고 바로 종료하면, 프로그램이 종료될 때 스레드도 종료된다. 따라서 프로그램은 스레드가 종료될때까지 기다려야 한다.

// 각 함수에 의해 반환된 void* 값이 여기에 저장된다.
void* result;
// pthread_join() 함수는 스레드가 종료될 때까지 기다린다.
int ste = pthread_join(t0, &result);
if (ste == -1) {
    puts("t0 스레드 종료 실패");
}

pthread_join() 함수는 스레드 함수가 반환된 값을 받아 void 포인터 변수에 저장한다. 스레드 모두가 실행을 완료해야 프로그램이 정상적으로 종료된다.

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

void error(char *msg) {
    fprintf(stderr, "%s: %s\n", msg, strerror(errno));
    exit(1);
}

void *does_not(void *a) {
    for (int i = 0; i < 5; i++) {
        sleep(1);
        puts("does not!");
    }
    // 반환할 값이 없으므로 그저 NULL을 반환
    return NULL;
}

void *does_too(void *a) {
    for (int i = 0; i < 5; i++) {
        sleep(1);
        puts("does too!");
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    pthread_t t0;
    pthread_t t1;
    if (pthread_create(&t0, NULL, does_not, NULL) == -1) {
        error("t0 스레드 생성 실패");
    }
    if (pthread_create(&t1, NULL, does_too, NULL) == -1) {
        error("t1 스레드 생성 실패");
    }

    void *result;
    if (pthread_join(t0, &result) == -1) {
        error("t0 스레드 종료 실패");
    }
    if (pthread_join(t1, &result) == -1) {
        error("t1 스레드 종료 실패");
    }

    return 0;
}

// 결과
/* 

does not!
does too!
does not!
does too!
does not!
does too!
does not!
does too!
does not!
does too!

*/
// gcc
gcc main.c -lpthread -o main.out

// cmake
find_package(Threads)
add_executable(main.out main.c)
target_link_libraries(main.out ${CMAKE_THREAD_LIBS_INIT})

pthread 라이브러리를 사용하고 있으므로 다음과 같이 프로그램을 컴파일하고 링크할 때 이 라이브러리를 꼭 링크해야 한다.

'Programming > C' 카테고리의 다른 글

[Programming/C] static 키워드  (0) 2020.06.21
[Programming/C] Mutex (상호배제, 상호배타)  (0) 2020.06.21
[Programming/C] 소켓과 서버  (0) 2020.06.20
[Programming/C] 시그널 (Signal)  (0) 2020.06.20
[Programming/C] pipe() 함수  (0) 2020.06.19
Comments