Как «многопоточный» код 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 ]

26 голосов
/ 11 октября 2010

Если задача очень распараллеливаемая, а ваш компилятор современный, вы можете попробовать OpenMP. http://en.wikipedia.org/wiki/OpenMP

19 голосов
/ 16 октября 2010

Одной из альтернатив многопоточности вашего кода будет использование pthreads (обеспечивает более точное управление, чем OpenMP).

Предполагая, x, y & result являются массивами глобальных переменных,

#include <pthread.h>

...

void *get_result(void *param)  // param is a dummy pointer
{
...
}

int main()
{
...
pthread_t *tid = malloc( ntimes * sizeof(pthread_t) );

for( i=0; i<ntimes; i++ ) 
    pthread_create( &tid[i], NULL, get_result, NULL );

... // do some tasks unrelated to result    

for( i=0; i<ntimes; i++ ) 
    pthread_join( tid[i], NULL );
...
}

(скомпилируйте свой код с помощью gcc prog.c -lpthread)

10 голосов
/ 14 октября 2010

Вы должны взглянуть на openMP для этого. Пример C / C ++ на этой странице похож на ваш код: https://computing.llnl.gov/tutorials/openMP/#SECTIONS

#include <omp.h>
#define N     1000

main ()
{

int i;
float a[N], b[N], c[N], d[N];

/* Some initializations */
for (i=0; i < N; i++) {
  a[i] = i * 1.5;
  b[i] = i + 22.35;
  }

#pragma omp parallel shared(a,b,c,d) private(i)
  {

  #pragma omp sections nowait
    {

    #pragma omp section
    for (i=0; i < N; i++)
      c[i] = a[i] + b[i];

    #pragma omp section
    for (i=0; i < N; i++)
      d[i] = a[i] * b[i];

    }  /* end of sections */

  }  /* end of parallel section */

}

Если вы предпочитаете не использовать openMP, вы можете использовать либо pthreads, либо клонировать / ждать напрямую.

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

Существуют некоторые накладные расходы при добавлении потоков для параллельной обработки, поэтому убедитесь, что вы предоставили каждому потоку достаточно работы, чтобы восполнить его. Обычно так и будет, но если для каждого потока требуется только 1 вычисление, а вычисления не так сложны, вы можете на самом деле замедлить процесс. В этом случае у вас всегда может быть меньше потоков, чем у процессоров.

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

9 голосов
/ 11 октября 2010

Если вы надеетесь обеспечить параллелизм для одного цикла для какого-либо научного вычисления или подобного, OpenMP, как говорит @Novikov, действительно является вашим лучшим выбором;это то, для чего оно было разработано.

Если вы хотите изучить более классический подход, который вы бы чаще видели в приложении, написанном на C ... В POSIX вы хотите pthread_create() et al.Я не уверен, каково ваше происхождение от параллелизма в других языках, но прежде чем углубляться в это, вы захотите достаточно хорошо знать примитивы синхронизации (мьютексы, семафоры и т. Д.), А также понимать, когданужно использовать их.Эта тема может быть целой книгой или набором ТАКИХ вопросов к себе.

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

Компилятор Intel C ++ на самом деле способен автоматически паралеллизировать ваш код. Это просто переключатель компилятора, который нужно включить. Хотя он не работает так же хорошо, как OpenMP (т. Е. Он не всегда успешен или результирующая программа работает медленнее). С сайта Intel: «Автоматическое распараллеливание, которое запускается параметром -parallel (Linux * OS и Mac OS * X) или / Qparallel (Windows * OS), автоматически определяет те структуры цикла, которые содержат параллелизм. Во время компиляции компилятор автоматически пытается деконструировать последовательности кода в отдельных потоках для параллельной обработки. Никаких других усилий программиста не требуется. "

3 голосов
/ 15 октября 2010

Хорошим упражнением для изучения параллельного программирования на любом языке будет работа над реализацией пула потоков.
В этом шаблоне вы создаете несколько потоков заранее. Эти темы рассматриваются как ресурс. Объект / структура пула потоков используется для назначения заданной пользователем задачи этим потокам для выполнения. Когда задача закончена, вы можете собрать ее результаты. Вы можете использовать пул потоков как шаблон проектирования общего назначения для параллелизма. Основная идея может выглядеть как

#define number_of_threads_to_be_created 42
// create some user defined tasks
Tasks_list_t* task_list_elem = CreateTasks();
// Create the thread pool with 42 tasks
Thpool_handle_t* pool = Create_pool(number_of_threads_to_be_created);

// populate the thread pool with tasks
for ( ; task_list_elem; task_list_elem = task_list_elem->next) {
   add_a_task_to_thpool (task_list_elem, pool);
}
// kick start the thread pool
thpool_run (pool);

// Now decide on the mechanism for collecting the results from tasks list.
// Some of the candidates are:
// 1. sleep till all is done (naive)
// 2. pool the tasks in the list for some state variable describing that the task has
//    finished. This can work quite well in some situations
// 3. Implement signal/callback mechanism that a task can use to signal that it has 
//    finished executing.

Механизм сбора данных из задач и количество потоков, используемых в пуле, следует выбирать в соответствии с вашими требованиями и возможностями оборудования и среды выполнения.
Также обратите внимание, что этот шаблон ничего не говорит о том, как вы должны «синхронизировать» свои задачи друг с другом / внешним окружением. Также обработка ошибок может быть немного хитрой (пример: что делать, если одна задача не выполняется). Эти два аспекта необходимо продумать заранее - они могут ограничить использование шаблона пула потоков.

О пуле потоков:
http://en.wikipedia.org/wiki/Thread_pool_pattern
http://docs.oracle.com/cd/E19253-01/816-5137/ggedn/index.html

Хорошая литература о том, как начать работу:
http://www.advancedlinuxprogramming.com/alp-folder/alp-ch04-threads.pdf

3 голосов
/ 11 октября 2010

В зависимости от ОС вы можете использовать потоки posix. Вместо этого вы можете реализовать многопоточность без использования стека, используя конечные автоматы. Есть действительно хорошая книга под названием «Встроенная многозадачность» Кейта Кертиса. Это просто аккуратно созданный набор операторов регистра переключателей. Прекрасно работает, я использовал его на всем: от Apple Mac, Rabbit Semiconductor, AVR, PC.

Вали

2 голосов
/ 17 октября 2010

Чтобы конкретно обратиться к части " автоматически многопоточности" вопроса ОП:

Один действительно интересный взгляд на программирование параллелизма был разработан для языка Cilk Plus изобретен MIT и теперь принадлежит Intel.По словам Википедии, идея состоит в том, что

"программист должен отвечать за выставление параллелизма, выявление элементов, которые могут безопасно выполняться параллельно; затем его следует оставить в среде выполнения,в частности, планировщик, чтобы во время выполнения решить, как на самом деле разделить работу между процессорами. "

Cilk Plus - это расширенный набор стандарта C ++.Он просто содержит несколько дополнительных ключевых слов (_Cilk_spawn, _Cilk_sync и _Cilk_for), которые позволяют программисту помечать части своей программы как параллелизуемые.Программист не предписывает запускать какой-либо код в новом потоке, он просто позволяет облегченному планировщику времени выполнения создавать новый поток тогда и только тогда, когда это действительно правильноделать в определенных условиях выполнения.

Чтобы использовать Cilk Plus, просто добавьте его ключевые слова в свой код и соберите с помощью компилятор Intel C ++ .

1 голос
/ 07 октября 2017

Вы можете использовать pthreads для выполнения многопоточности в C. Вот простой пример, основанный на pthreads.

#include<pthread.h>
#include<stdio.h>

void *mythread1();  //thread prototype
void *mythread2();

int main(){
    pthread_t thread[2];
    //starting the thread
    pthread_create(&thread[0],NULL,mythread1,NULL);
    pthread_create(&thread[1],NULL,mythread2,NULL);
    //waiting for completion
    pthread_join(thread[0],NULL);
    pthread_join(thread[1],NULL);


    return 0;
}

//thread definition
void *mythread1(){
    int i;
    for(i=0;i<5;i++)
        printf("Thread 1 Running\n");
}
void *mythread2(){
    int i;
    for(i=0;i<5;i++)
        printf("Thread 2 Running\n");
}

Справка: Программа C для реализации многопоточности-многопоточности в C

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

Если итерация в цикле не зависит от предыдущих, то есть очень простой подход: попробуйте многопоточность, а не многопоточность.

Допустим, у вас есть 2 ядра, а ntimes равно 100, затем 100/2 = 50, поэтому создайте 2 версии программы, в которой первая итерация от 0 до 49, другая от 50 до 99. ядра должны быть довольно заняты.

Это очень упрощенный подход, но вам не нужно возиться с созданием потоков, синхронизацией и т. Д.

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