我有一个用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)
小智 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写入线程中的同一文件.
如果你希望为某种科学计算或类似的循环提供并发,OpenMP as @Novikov说真的是你最好的选择; 这就是它的设计目标.
如果您正在寻找更经典的方法,那么您通常会在用C语言编写的应用程序中看到这种方法...在POSIX上你想要的pthread_create()等等.我不确定你的背景可能与其他语言的并发性有什么关系,但在深入研究之前,你会想要很好地了解你的同步原语(互斥,信号量等),以及了解你何时会需要使用它们.这个话题可能是一整本书或一组SO问题本身.
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)
编译并运行:
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 上游.