理解C中的线程

理解C中的线程,c,multithreading,pthreads,C,Multithreading,Pthreads,我有一个C语言的数学函数,它可以计算很多复杂的数学。该函数有一个标题: double doTheMath(struct example *e, const unsigned int a, const unsigned int b, const unsigned int c) { /* ... lots of math */ } 我想同时调用这个函数100次。我阅读了关于pthreads的文章,认为这是一个很好的解决方案,可以实现我想要实现的目标。我的想

我有一个C语言的数学函数,它可以计算很多复杂的数学。该函数有一个标题:

double doTheMath(struct example *e, const unsigned int a,
                 const unsigned int b, const unsigned int c)
{
    /* ... lots of math */
}
我想同时调用这个函数100次。我阅读了关于
pthreads
的文章,认为这是一个很好的解决方案,可以实现我想要实现的目标。我的想法如下:

pthread_t tid[100];
pthread_mutex_t lock;

if (pthread_mutex_init(&lock, NULL) != 0)
    {
        printf("\n mutex init failed\n");
        return 1;
    }

for(i=0; i<100; i++) {
    pthread_create(&(tid[i]), NULL, &doTheMath, NULL);
    pthread_mutex_lock(&lock);
    d += doTheMath(args);
    pthread_mutex_unlock(&lock); 
}

pthread_mutex_destroy(&lock);
它将创建我的100个线程(我的机器肯定能够运行),我不需要互斥,所以它将同时调用我的函数100次

cPU核心呢?当我这样做的时候,我所有的CPU核心都会满负荷吗

只有一个函数调用将只加载我CPU的一个核心,创建并运行100个(或更多)线程,调用我的函数将加载我所有的CPU核心-我在这里吗

如何传递给doTheMath它需要的所有参数

您必须创建一个代理函数,该函数具有
pthread\u create
可接受的签名,该签名调用其主体中的实际函数:

struct doTheMathArgs {
    struct example *e;
    const unsigned int a, b, c;
};

callDoTheMath(void *data) {
    struct doTheMathArgs *args = data;

    doTheMath(args->e, args->a, args->b, args->c);
}

...

struct doTheMathArgs dtma;
dtma.e = ...;
...
dtma.c = ...;

pthread_create(&(tid[i]), NULL, &callDoTheMath, (void *) &dtma);
在这里使用线程真的有意义吗?这会像我希望的那样工作吗?我真的无法理解,当我用互斥锁锁定我的函数调用时,它不会允许同时调用我的函数100次,对吗?那我该怎么做呢

除非你在一台一次能运行100个线程的计算机上工作,否则你的代码会减慢整个过程。你应该选择你的maschine能够运行的多个线程

要回答问题的进一步部分,您必须告诉我们您的
doTheMath
函数是如何工作的。此函数的每次调用是否完全独立于其他调用?调用顺序重要吗

如何传递给doTheMath它需要的所有参数

您必须创建一个代理函数,该函数具有
pthread\u create
可接受的签名,该签名调用其主体中的实际函数:

struct doTheMathArgs {
    struct example *e;
    const unsigned int a, b, c;
};

callDoTheMath(void *data) {
    struct doTheMathArgs *args = data;

    doTheMath(args->e, args->a, args->b, args->c);
}

...

struct doTheMathArgs dtma;
dtma.e = ...;
...
dtma.c = ...;

pthread_create(&(tid[i]), NULL, &callDoTheMath, (void *) &dtma);
在这里使用线程真的有意义吗?这会像我希望的那样工作吗?我真的无法理解,当我用互斥锁锁定我的函数调用时,它不会允许同时调用我的函数100次,对吗?那我该怎么做呢

除非你在一台一次能运行100个线程的计算机上工作,否则你的代码会减慢整个过程。你应该选择你的maschine能够运行的多个线程


要回答问题的进一步部分,您必须告诉我们您的
doTheMath
函数是如何工作的。此函数的每次调用是否完全独立于其他调用?调用顺序重要吗?

是的,您可以通过线程来实现这一点。调用它100次是否真的会加快速度是一个单独的问题,因为一旦所有CPU内核都已完全加载,尝试一次运行更多的东西可能会降低速度,而不是提高速度,因为处理器缓存效率会降低。对于CPU密集型任务,最佳线程数可能是CPU核心数(或更多)

关于你的具体问题:

Q1:使用
phtread\u create
时,最后一个参数是
void*arg
,它是传递给线程的不透明指针。您通常会将其用作指向包含要传递的参数的
结构的指针。这也可用于返回计算的总和。请注意,您必须将
do\u math
包装在合适的第二个函数中,以便其签名(返回值和参数)与
pthread\u create
所期望的一样

问题2。请参见上面的一般警告,因为线程太多,但使用这是一种有用的技术

具体而言:

  • 您不希望以目前的方式使用互斥锁。互斥仅用于保护访问公共数据的部分代码(关键部分)

  • 您可以创建一个线程来调用
    doTheMath
    ,然后也可以直接从主线程调用
    doTheMath
    。这是不正确的。相反,您应该只创建所有线程(在一个循环中),然后运行另一个循环以等待每个线程完成,并对返回的答案求和


是的,您可以使用线程执行此操作。调用它100次是否真的会加快速度是一个单独的问题,因为一旦所有CPU内核都已完全加载,尝试一次运行更多的东西可能会降低速度,而不是提高速度,因为处理器缓存效率会降低。对于CPU密集型任务,最佳线程数可能是CPU核心数(或更多)

关于你的具体问题:

Q1:使用
phtread\u create
时,最后一个参数是
void*arg
,它是传递给线程的不透明指针。您通常会将其用作指向包含要传递的参数的
结构的指针。这也可用于返回计算的总和。请注意,您必须将
do\u math
包装在合适的第二个函数中,以便其签名(返回值和参数)与
pthread\u create
所期望的一样

问题2。请参见上面的一般警告,因为线程太多,但使用这是一种有用的技术

具体而言:

  • 您不希望以目前的方式使用互斥锁。互斥仅用于保护访问公共数据的部分代码(关键部分)

  • 您可以创建一个线程来调用
    doTheMath
    ,然后也可以直接从主线程调用
    doTheMath
    。这是不正确的。相反,您应该只创建所有线程(在一个循环中),然后运行另一个循环以等待每个线程完成,并对返回的答案求和


这个问题缺少大量重要细节。调用该函数的效果如何?你想对结果做什么?什么是
d