c - 提高多线程程序的速度

标签 c multithreading

我有一个简单的问题:我需要创建三个线程并在每个线程中执行特定操作。第一个线程需要添加100array[0]并减去 101来自 array[1] ,第二个线程需要添加200array[1]并减去 201来自 array[2]最后第三个线程需要添加 300array[2]并减去 301来自 array[0] .

下面是正确的解决方案,但运行时间非常长。如果我使用一个线程执行此任务,运行时间将减少 1 秒,但三个线程将运行时间增加到大约 10 秒(+- 2 秒)。 问题是什么?我认为具有三个线程的解决方案必须更快。可能是我以错误的方式使用了互斥体?

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

enum {SIZE = 3, ITER = 1000000};
double array[SIZE] = {};
pthread_t threads[SIZE];

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *func(void *arg) {
    int n = *(int *)arg;
    int tmp = 100 * (n + 1),
        tmp2 = tmp + 1;
    for (int i = 0; i != ITER; ++i) {
        pthread_mutex_lock(&mutex);
        array[n % SIZE] += tmp;
        array[(n + 1) % SIZE] -= tmp2;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    int n[SIZE];
    for (int i = 0; i != SIZE; ++i) {
        n[i] = i;
        pthread_create(threads + i, NULL, func, n + i); 
    }

    for (int i = 0; i != SIZE; ++i) {
        pthread_join(threads[i], NULL);
    }

    for (int i = 0; i != SIZE; ++i) {
        printf("%.10g ", array[i]);
    }
    printf("\n");

    return 0;
}

最佳答案

虽然互斥锁会带来很大的开销,但这并不是您速度变慢的原因。原因是互斥锁序列化了执行。在任何时间点只能运行一个线程,因为只有一个线程可以持有互斥量。所以你在实践中得到的是一个带有额外同步开销的串行执行。

您希望您的线程像这样运行(X 代表一个正在运行的线程)。

Thread1: |X|X|X|X|X|
Thread2: |X|X|X|X|X|
Thread3: |X|X|X|X|X|

但是,你得到的是这样的:

Thread1: |X| | |X| | |X| | |X| | |X| | |
Thread2: | |X| | |X| | |X| | |X| | |X| |
Thread3: | | |X| | |X| | |X| | |X| | |X|

在每个时隙,只有一个线程运行。

有几种方法:

  1. 您可以按照每个线程独占访问数组的不同索引的方式对您的操作进行分区。这样你就可以完全避免使用互斥量。但是,这需要彻底重新设计您的解决方案。

  2. 您可以让每个线程处理数组的本地副本,然后将所有线程的结果合并到一个线程上。这要简单得多,因为您所要做的就是将数据复制到线程并摆脱对互斥锁的需要。

  3. 您可以为每个数组索引使用一个互斥体,但这似乎有点极端,因为内存和时间开销很高,因为冲突概率实际上非常低。

总而言之,使用选项 1 将产生最佳性能,但使用选项 2 与串行版本相比将产生显着的加速,并且设计工作相对较少。选项 3 是不明智的。

关于c - 提高多线程程序的速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44122721/

相关文章:

c - wcscmp - 使用此函数时发生访问冲突

c - 当在函数中声明相同变量时全局变量不起作用

java - 我可以对 subscribeOn 方法和异步任务使用相同的执行器吗

ios - 如何在 Swift 中将任务发送到后台队列?

c++ - 线程 C++ 中的瓶颈

c# - 在 HashSet<T> 中是否包含线程安全

c 程序绕过 gets()

C 字符串比较失败?

c - 位运算,保存位,8位后扩展

Java 线程和 POSIX 线程,用户级还是内核级?