Как мне начать темы в простом C? - PullRequest
47 голосов
/ 11 сентября 2008

Я использовал fork () в C, чтобы запустить другой процесс. Как начать новую тему?

Ответы [ 6 ]

52 голосов
/ 11 сентября 2008

Поскольку вы упомянули fork (), я предполагаю, что вы работаете в Unix-подобной системе, и в этом случае потоки POSIX (обычно называемые pthreads) - это то, что вы хотите использовать.

В частности, pthread_create () - это функция, необходимая для создания нового потока. Его аргументы:

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

Первый аргумент - это возвращенный указатель на идентификатор потока. Второй аргумент - это аргументы потока, которые могут быть NULL, если вы не хотите запускать поток с определенным приоритетом. Третий аргумент - это функция, выполняемая потоком. Четвертый аргумент - это единственный аргумент, передаваемый функции потока при ее выполнении.

12 голосов
/ 11 сентября 2008

AFAIK, ANSI C не определяет многопоточность, но доступны различные библиотеки.

Если вы работаете в Windows, создайте ссылку на msvcrt и используйте _beginthread или _beginthreadex.

Если вы работаете на других платформах, проверьте библиотеку pthreads (я уверен, что есть и другие).

8 голосов
/ 11 сентября 2008

Потоки не являются частью стандарта C, поэтому единственный способ использовать потоки - это использовать некоторую библиотеку (например: потоки POSIX в Unix / Linux, _beginthread / _beginthreadex, если вы хотите использовать среду выполнения C из этого потока или просто CreateThread Win32 API)

7 голосов
/ 11 сентября 2008

pthreads - хорошее начало, смотрите здесь

2 голосов

нити C11 + C11 atomic_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

main.c

#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);
}

Скомпилируйте и запустите:

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

Возможный вывод:

atomic     10000
non-atomic 4341

Скорее всего, неатомарный счетчик будет меньше атомного из-за быстрого доступа через потоки к неатомарной переменной.

См. Также: Как сделать атомарный инкремент и извлечь в 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>

, поэтому мы видим, что атомарный инкремент выполняется на уровне инструкций с префиксом f0 lock .

С 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, который выполняется до тех пор, пока не будет успешно сохранено хранилище * 1048.

Benchmark

TODO. Создайте эталонный тест, чтобы показать, что атомная медленнее.

POSIX-темы

main.c

#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

Проверено на Ubuntu 18.04. GitHub upstream .

GCC __atomic_* встроенные

Для тех, у кого нет C11, вы можете добиться атомарного приращения с помощью расширений __atomic_* GCC.

main.c

#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 threads".

Протестировано в Ubuntu 16.04, amd64, GCC 6.4.0.

2 голосов
/ 11 сентября 2008

Проверьте библиотеку pthread (поток POSIX).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...