Как «многопоточный» код C - PullRequest
36 голосов
/ 11 октября 2010

У меня есть приложение для обработки чисел, написанное на C. Это своего рода основной цикл, который для каждого значения вызывает, для увеличения значения «i», функцию, которая выполняет некоторые вычисления. Я читал о многопоточности и собираюсь немного узнать об этом в C. Интересно, может ли какой-нибудь общий код, такой как мой, быть автоматически многопоточным и как.

Спасибо

P.D. Чтобы получить представление о моем коде, скажем, что он выглядит примерно так:

main(...)
for(i=0;i<=ntimes;i++)get_result(x[i],y[i],result[i]);

...

void get_result(float x,float y,float result){
  result=sqrt(log (x) + log (y) + cos (exp (x + y));
(and some more similar mathematical operations)
}

Ответы [ 12 ]

1 голос
/ 18 октября 2010

Ваш код не является многопоточным автоматически компилятором, если это был ваш вопрос.Обратите внимание, что сами стандарты C ничего не знают о многопоточности, поскольку то, можете ли вы использовать многопоточность, зависит не от языка, который вы используете для кодирования, а от платформы назначения, для которой вы кодируете.Код, написанный на C, может работать практически на всем, для чего существует компилятор C.Компилятор переменного тока даже существует для компьютера C64 (почти полностью соответствует ISO-99);однако для поддержки нескольких потоков платформа должна иметь операционную систему, поддерживающую это, и обычно это означает, что должна присутствовать хотя бы определенная функциональность ЦП.Операционная система может выполнять многопоточность почти исключительно в программном обеспечении, это будет ужасно медленно и не будет защиты памяти, но это возможно, однако даже в этом случае вам нужны как минимум программируемые прерывания.

Так какнаписание многопоточного C-кода полностью зависит от операционной системы вашей целевой платформы.Существуют системы, соответствующие POSIX (OS X, FreeBSD, Linux и т. Д.), И системы, для которых есть своя библиотека (Windows).В некоторых системах есть больше, чем просто библиотека (например, OS X имеет библиотеку POSIX, но есть и менеджер углеродных потоков, который вы можете использовать в C (хотя я думаю, что это довольно унаследовано в настоящее время).

Конечносуществуют кросс-платформенные библиотеки потоков, и некоторые современные компиляторы поддерживают такие вещи, как OpenMP, где компилятор автоматически создает код для создания потоков на выбранной вами целевой платформе, но не многие компиляторы поддерживают его, и те, которые поддерживают его, обычно не поддерживаютКак правило, вы получаете самую широкую поддержку системы, используя потоки POSIX, чаще называемые «pthreads». Единственной основной платформой, не поддерживающей ее, является Windows, и здесь вы можете использовать бесплатные сторонние библиотеки, такие как , эту . Несколькосуществуют и другие порты ( у Cygwin наверняка есть). Если у вас когда-нибудь будет пользовательский интерфейс, вы можете использовать кросс-платформенную библиотеку, такую ​​как wxWidgets или SDL , оба предлагают согласованную многопоточную поддержку для всехпортированные платформы.

0 голосов

Потоки C11 в glibc 2.28.

Протестировано в Ubuntu 18.04 (glibc 2.27) путем компиляции glibc из источника: Несколько библиотек glibc на одном хосте

Пример из: https://en.cppreference.com/w/c/language/atomic

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

atomic_int acnt;
int cnt;

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

int main(void)
{
    thrd_t thr[10];
    for(int n = 0; n < 10; ++n)
        thrd_create(&thr[n], f, NULL);
    for(int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);

    printf("The atomic counter is %u\n", acnt);
    printf("The non-atomic counter is %u\n", cnt);
}

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

gcc -std=c11 main.c -pthread
./a.out

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

The atomic counter is 10000
The non-atomic counter is 8644

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

TODO: разберите и посмотрите, с чем ++acnt; компилируется.

потоки 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 pthread_mutex.c -pthread
./a.out
./a.out 1

Первый запуск работает нормально, второй завершается неудачно из-за отсутствия синхронизации.

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

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