如何"多线程"C代码

Ope*_*way 36 c multithreading

我有一个用C编写的数字运算应用程序.它是一种主循环,对于每个值调用,增加"i"的值,执行一些计算的函数.我读到了多线程,我正在考虑在C中学习一点.我想知道像我这样的某些通用代码是否可以自动多线程以及如何.

谢谢

PD为了了解我的代码,让我们说它是这样的:

main(...)
for(i=0;i<=ntimes;i++)get_result(x[i],y[i],result[i]);
Run Code Online (Sandbox Code Playgroud)

...

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)
}
Run Code Online (Sandbox Code Playgroud)

Nov*_*kov 26

如果任务是高度可并行化的并且您的编译器是现代的,那么您可以尝试OpenMP.http://en.wikipedia.org/wiki/OpenMP


小智 20

多线程代码的一种替代方法是使用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 );
...
}
Run Code Online (Sandbox Code Playgroud)

(编译代码gcc prog.c -lpthread)


nat*_*ose 10

你应该看一下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 */

}
Run Code Online (Sandbox Code Playgroud)

如果您不想使用openMP,可以直接使用pthreads或clone/wait.

无论您选择哪种路由,您只需将数组划分为每个线程将处理的块.如果你的所有处理都是纯粹的计算(正如你的示例函数所建议的那样),那么你应该只拥有与逻辑处理器一样多的线程.

添加线程进行并行处理会有一些开销,因此请确保为每个线程提供足够的工作来弥补它.通常你会,但是如果每个线程最终只进行1次计算,并且计算并不困难,那么你实际上可能会减慢速度.如果是这种情况,您可以始终拥有比处理器少的线程.

如果你的工作中确实有一些IO,那么你可能会发现拥有比处理器更多的线程是一种胜利,因为当一个线程可能阻塞等待某些IO完成时,另一个线程可以进行其计算.但是,您必须小心将IO写入线程中的同一文件.


asv*_*kau 9

如果你希望为某种科学计算或类似的循环提供并发,OpenMP as @Novikov说真的是你最好的选择; 这就是它的设计目标.

如果您正在寻找更经典的方法,那么您通常会在用C语言编写的应用程序中看到这种方法...在POSIX上你想要的pthread_create()等等.我不确定你的背景可能与其他语言的并发性有什么关系,但在深入研究之前,你会想要很好地了解你的同步原语(互斥,信号量等),以及了解你何时会需要使用它们.这个话题可能是一整本书或一组SO问题本身.


Cir*_*四事件 6

glibc 2.28 中的 C11 线程。

在 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);
}
Run Code Online (Sandbox Code Playgroud)

GitHub 上游.

编译并运行:

gcc -std=c11 main.c -pthread
./a.out
Run Code Online (Sandbox Code Playgroud)

可能的输出:

The atomic counter is 10000
The non-atomic counter is 8644
Run Code Online (Sandbox Code Playgroud)

由于跨线程对非原子变量的快速访问,非原子计数器很可能小于原子计数器。

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;
}
Run Code Online (Sandbox Code Playgroud)

编译并运行:

gcc -std=c99 pthread_mutex.c -pthread
./a.out
./a.out 1
Run Code Online (Sandbox Code Playgroud)

第一次运行正常,第二次由于缺少同步而失败。

在 Ubuntu 18.04 上测试。GitHub 上游.