programing

일반 C에서 스레드를 시작하려면 어떻게 해야 합니까?

firstcheck 2022. 7. 5. 00:14
반응형

일반 C에서 스레드를 시작하려면 어떻게 해야 합니까?

다른 프로세스를 시작하기 위해 C에서 fork()를 사용했습니다.새 스레드를 시작하려면 어떻게 해야 합니까?

fork()를 언급하셨기 때문에 Unix와 같은 시스템 상에 있을 것으로 생각됩니다.이 경우 POSIX 스레드(일반적으로 pthreads라고 불립니다)는 사용하고 싶은 것입니다.

특히 pthread_create()는 새로운 스레드를 작성하기 위해 필요한 함수입니다.인수는 다음과 같습니다.

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

첫 번째 인수는 스레드 ID에 대한 반환된 포인터입니다.두 번째 인수는 스레드 인수입니다.특정 priority로 스레드를 시작하지 않는 한 NULL로 할 수 있습니다.세 번째 인수는 스레드에 의해 실행되는 함수입니다.네 번째 인수는 스레드 함수가 실행될 때 전달되는 단일 인수입니다.

AFIK, ANSI C는 스레드를 정의하지 않지만 다양한 라이브러리를 사용할 수 있습니다.

Windows 상에서 동작하고 있는 경우는, msvcrt 에 링크 해 _beginthread 또는 _beginthreadex 를 사용합니다.

다른 플랫폼에서 실행 중인 경우 pthreads 라이브러리를 확인하십시오(다른 플랫폼도 있을 것입니다).

스레드 + C11 스레드 + C11atomic_int

glibc 2.28에 추가되었습니다.Ubuntu 18.10 amd64(glic 2.28과 함께 제공) 및 Ubuntu 18.04(glibc 2.27과 함께 제공)에서 소스로부터 glibc 2.28을 컴파일하여 테스트 완료:단일 호스트에 여러 glibc 라이브러리

예: https://en.cppreference.com/w/c/language/atomic에서 수정

메인

#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>

atomic_int atomic_counter;
int non_atomic_counter;

int mythread(void* thr_data) {
    (void)thr_data;
    for(int n = 0; n < 1000; ++n) {
        ++non_atomic_counter;
        ++atomic_counter;
        // for this example, relaxed memory order is sufficient, e.g.
        // atomic_fetch_add_explicit(&atomic_counter, 1, memory_order_relaxed);
    }
    return 0;
}

int main(void) {
    thrd_t thr[10];
    for(int n = 0; n < 10; ++n)
        thrd_create(&thr[n], mythread, NULL);
    for(int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);
    printf("atomic     %d\n", atomic_counter);
    printf("non-atomic %d\n", non_atomic_counter);
}

GitHub 업스트림

컴파일 및 실행:

gcc -ggdb3 -std=c11 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out

가능한 출력:

atomic     10000
non-atomic 4341

non-atomic 카운터는 non-atomic 변수에 대한 스레드 간의 레이시 액세스 때문에 atomic 카운터보다 작을 가능성이 매우 높습니다.

다음 항목도 참조하십시오.어떻게 원자 증분을 하고 C를 가져오죠?

분해 분석

분해 방법:

gdb -batch -ex "disassemble/rs mythread" main.out

내용:

17              ++non_atomic_counter;
   0x00000000004007e8 <+8>:     83 05 65 08 20 00 01    addl   $0x1,0x200865(%rip)        # 0x601054 <non_atomic_counter>

18              __atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST);
   0x00000000004007ef <+15>:    f0 83 05 61 08 20 00 01 lock addl $0x1,0x200861(%rip)        # 0x601058 <atomic_counter>

따라서 원자 인크리먼트는 명령 수준에서 잠금프리픽스를 사용하여 이루어집니다.

★★★★★★★★★★★★★★★★ aarch64-linux-gnu-gcc 8.2.0이 나옵니다.

11              ++non_atomic_counter;
   0x0000000000000a28 <+24>:    60 00 40 b9     ldr     w0, [x3]
   0x0000000000000a2c <+28>:    00 04 00 11     add     w0, w0, #0x1
   0x0000000000000a30 <+32>:    60 00 00 b9     str     w0, [x3]

12              ++atomic_counter;
   0x0000000000000a34 <+36>:    40 fc 5f 88     ldaxr   w0, [x2]
   0x0000000000000a38 <+40>:    00 04 00 11     add     w0, w0, #0x1
   0x0000000000000a3c <+44>:    40 fc 04 88     stlxr   w4, w0, [x2]
   0x0000000000000a40 <+48>:    a4 ff ff 35     cbnz    w4, 0xa34 <mythread+36>

'아토믹 버전'이 .cbnz「」까지 stlxr은 이 LDADD 할 수 .ARMv8.1의 LDADD에 관한 것입니다.

은 C로 C++로 얻을 수 있는 .std::atomic: std:: atomic이란 정확히 무엇입니까?

벤치마크

TODO. 원자 속도가 더 느리다는 걸 보여주기 위해 벤치마크를 작성하세요

POSIX 스레드

메인

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <stdlib.h>
#include <pthread.h>

enum CONSTANTS {
    NUM_THREADS = 1000,
    NUM_ITERS = 1000
};

int global = 0;
int fail = 0;
pthread_mutex_t main_thread_mutex = PTHREAD_MUTEX_INITIALIZER;

void* main_thread(void *arg) {
    int i;
    for (i = 0; i < NUM_ITERS; ++i) {
        if (!fail)
            pthread_mutex_lock(&main_thread_mutex);
        global++;
        if (!fail)
            pthread_mutex_unlock(&main_thread_mutex);
    }
    return NULL;
}

int main(int argc, char **argv) {
    pthread_t threads[NUM_THREADS];
    int i;
    fail = argc > 1;
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_create(&threads[i], NULL, main_thread, NULL);
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_join(threads[i], NULL);
    assert(global == NUM_THREADS * NUM_ITERS);
    return EXIT_SUCCESS;
}

컴파일 및 실행:

gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out
./main.out 1

첫 번째 실행은 정상적으로 동작하고 두 번째 실행은 동기화 누락으로 인해 실패합니다.

POSIX 표준 원자 연산이 없는 것 같습니다: UNIX Portable Atomic Operations

Ubuntu 18.04로 테스트.GitHub 업스트림

GCC__atomic_*짜넣기

C11을 탑재하지 않은 사용자에게는 C11을 사용하여 원자적인 증가를 실현할 수 있습니다.__atomic_*GCC 내선번호

메인

#define _XOPEN_SOURCE 700
#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>

enum Constants {
    NUM_THREADS = 1000,
};

int atomic_counter;
int non_atomic_counter;

void* mythread(void *arg) {
    (void)arg;
    for (int n = 0; n < 1000; ++n) {
        ++non_atomic_counter;
        __atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST);
    }
    return NULL;
}

int main(void) {
    int i;
    pthread_t threads[NUM_THREADS];
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_create(&threads[i], NULL, mythread, NULL);
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_join(threads[i], NULL);
    printf("atomic     %d\n", atomic_counter);
    printf("non-atomic %d\n", non_atomic_counter);
}

컴파일 및 실행:

gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out

출력 및 생성된 어셈블리: "C11 스레드" 예시와 동일합니다.

Ubuntu 16.04 amd64, GCC 6.4.0에서 테스트 완료.

pthreads는 좋은 시작입니다. 여기를 보세요.

스레드는 C 표준이 아니기 때문에 스레드를 사용하는 유일한 방법은 일부 라이브러리(예를 들어 Unix/Linux의 POSIX 스레드, 스레드로부터의 C-런타임을 사용하려면 _beginthread/_beginthreadex 또는 CreateThread Win32 API)를 사용하는 것입니다.

pthread(POSIX 스레드) 라이브러리를 확인합니다.

언급URL : https://stackoverflow.com/questions/56810/how-do-i-start-threads-in-plain-c

반응형