Multi Thread

immanuelk1m·2024년 4월 22일
0

Overview

Thread 구성 요소

  • TBC block : thread id, state, stack pointer, PC, PCB, thread's regester value

Why

  • 프로세스 하나로 처리하기 어려움 해결 (웹서버 같은 경우 동시 요청 여러 개를 처리해야 함)
  • 확장성 (Scalability) : 여러 프로레서 아키텍쳐 사용 가능
  • 응답성 (Responsiveness) : 하나의 스레드가 블로킹되어도 다른 스레드는 계속 작업을 수행
  • 자원 공유 (Resource Sharing) : 스레드는 같은 프로세스 내에서 메모리를 자동으로 공유해 IPC 보다 편리
  • 경제성 (Economy) : 스레드 생성은 프로세스에 비해 약 30배 더 빠르고 경제적

Multi core / Multiprocessor

Reference

Concurrency / Parallelism

Multicore Programming

  • Data parallelism : 데이터를 쪼개서 동일 연산을 thread에 분산 시킴
  • Task parallelism : 각각의 thread가 각자의 역할을 동시에 함

Amdahl's Law

  • 병렬 처리로 얻을 수 있는 최대 성능 향상을 계산

  • 실제 speedup은 이보다 낮은 수준

단일 cpu 작업 100m 이 걸린다고 할 때, 순차적 작업은 20% 병렬 가능 작업 80% 가능하다고 하고, cpu 수는 4개 일 때 최적화된 실행 시간은?

  • 1000.2 + 1000.8/4 = 20m

Multithreading models

User thread / Kernel thread

  • User thread : thread 개념을 프로그래밍 레벨에서 추상화 한 것
  • Kernel thread : CPU에서 실제로 실행되는 단위 CPU Scheduling 단위
    Reference
    Youtube

Scheduler Activation / Lightweight process

Thread libraries

Thread library

  • 프로그래머가 스레드를 생성하고 관리하기 위한 일련의 API
  • 유저 레벨 라이브러리와 커널 레벨 라이브러리가 있음

POSIX Pthreads

Implicit Threading

Thread Pools

  • 미리 생성된 스레드의 집합
  • 작업 요청이 있을 때 활성화되어 작업을 수행하고, 완료되면 다시 풀로 돌아가 다음 작업을 기다림

OpenMP

  • 공유 메모리 환경에서 병렬 프로그래밍을 지원하는 API와 컴파일러 지시문 집합
#pragma omp parallel for
for(i=0; i<N; i++) {
    c[i] = a[i] + b[i];
}
//  a[i] + b[i]의 각 연산을 여러 스레드에 분배하여 c 배열을 병렬로 채움

Threading issues

fork() / exec()

https://woochan-autobiography.tistory.com/207

Cancellation


pthread_t pthread_setcanceltype(int type, int* oldtype)

- PTHREAD_CANCEL_ASYCHRONUS : 호출 시 즉시 종료
- PTHREAD_CANCEL_DEFERED : pthread_testcancel() 호출지점 이후 종료

pthread_t pthread_setcancelstate(int state, int* oldstate)

- PTHREAD_CANCEL_DISABLE : 취소 요청을 받더라도 즉시 취소되지 않고, 
  취소 요청이 보류(pending) 상태

- PTHREAD_CANCEL_ENABLE : 스레드는 취소 요청을 받는 즉시 해당 요청을 처리
 (PTHREAD_CANCEL_ASYNCHRONOUS / PTHREAD_CANCEL_DEFERRED)

Signal handling

UNIX와 UNIX 계열 시스템에서 프로세스에 특정 이벤트가 발생했다는 것을 알리기 위해 사용되는 메커니즘

  • 생성된 신호는 해당 프로세스로 전달되며, 프로세스는 신호를 어떻게 처리할지 결정

동기적 신호(Synchronous Signals):

프로세스가 실행 중인 동안 자체적으로 발생한 이벤트에 의해 생성

  • 잘못된 메모리 접근이나 0으로 나누기와 같은 연산

비동기적 신호(Asynchronous Signals):

외부 이벤트나 다른 프로세스의 행위에 의해 생성

  • 사용자가 키보드에서 Ctrl-C를 누르거나 다른 프로세스가 현재 프로세스에
    신호를 보내는 경우 비동기적 신호가 발생

Ex

  • SIGINT : Ctrl + C (ignore 가능)
  • SIGKILL : Ctrl + <- (ignore 불가능)

How to Deliver SIGNAL to THREAD??

  • 어떤 signal을 보낼 것인지
  • 모든 thread 인지, 특정 thread인지
  • 특정 스레드를 신호 수신 전담으로 지정할 것인지
pthread_kill(tid, signal)
- 스레드 식별자 tid의 스레드에 signal 신호를 전송

Thread-local storage

각 스레드가 자신만의 데이터 복사본을 가지고 작업할 수 있도록 하는 메커니즘
TLS를 사용하면 각 스레드가 독립적으로 데이터를 접근하고 수정할 수 있어
데이터 경합이나 동기화 문제를 효과적으로 방지 가능

__thread int tls;

// 주로 전역 변수에 선언

TLS와 로컬 변수의 차이점

로컬 변수: 함수가 호출될 때 생성되고 함수가 리턴할 때 소멸, 함수 내에서만 접근 가능
TLS: 스레드가 시작될 때 생성되고 스레드가 종료될 때까지 유지, 스레드 내의 어떤 함수에서든 접근 가능

TLS와 정적 데이터의 유사성

정적 데이터 : 프로세스 내의 모든 스레드 간에 공유
TLS : 변수는 각 스레드에 고유, 쓰레드간 독립적으로 격리 되어있음

TLS의 유용성

스레드 풀을 사용하는 경우와 같이 스레드 생성 과정을 직접 제어할 수 없는 환경에서 TLS는 특히 유용함. 스레드 풀에서는 여러 작업을 동시에 처리할 수 있는 스레드들이 미리 생성되어 있으며, 각 스레드는 재사용될 수 있음. 이런 경우, 각 스레드가 독립적인 작업 상태를 유지해야 하는데 도움을 줌.

Operating system examples

Window XP Threads

  • one-to-one mapping
  • context of thread : register set, user stack & kernel stack, private storage area

Linux Threads

clone() 시스템 호출은 리눅스에서 프로세스 또는 스레드를 생성할 때 사용하는 함수
fork()와 유사하지만, clone()은 호출하는 프로세스의 자식 프로세스와 공유할 리소스의 범위를 사용자가 더 세밀하게 제어할 수 있는 옵션을 제공

clone()의 기능과 특징
clone() 함수는 특정 플래그를 사용하여, 자식 프로세스와 공유할 리소스를 정확하게 지정할 수 있음

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int childFunction(void* arg) {
    printf("Child process\n");
    return 0;
}

int main() {
    const int STACK_SIZE = 65536;  // 스택 크기 할당
    char* stack = malloc(STACK_SIZE);
    if (!stack) {
        perror("Failed to allocate stack");
        exit(1);
    }

    // clone() 호출, childFunction을 자식 프로세스의 시작 포인트로 설정
    int child_pid = clone(childFunction, stack + STACK_SIZE, 
                          SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM, NULL);
    
    if (child_pid == -1) {
        perror("clone");
        exit(1);
    }

    printf("Parent process\n");
    wait(NULL); // 자식 프로세스 종료 대기
    free(stack);
    return 0;
}

// 
// SIGCHLD: 자식 프로세스가 종료되었을 때 부모 프로세스에게 SIGCHLD 신호를 보내도록 설정
// CLONE_FS: 파일 시스템 관련 정보를 부모 프로세스와 공유
// CLONE_FILES: 파일 디스크립터를 부모 프로세스와 공유
// CLONE_SIGHAND: 신호 처리기를 부모 프로세스와 공유
// CLONE_VM: 가상 메모리 공간을 부모 프로세스와 공유
profile
개발 새발

0개의 댓글

관련 채용 정보