计算机不会同时执行多线程程序

Posted

技术标签:

【中文标题】计算机不会同时执行多线程程序【英文标题】:Computer won't execute multi-thread program simultaneously 【发布时间】:2021-11-26 08:07:24 【问题描述】:

我使用 pthreads 编写了一个简单的 c 程序。在我的笔记本电脑上,多线程程序按预期运行。在桌面上运行时,程序的执行是不寻常的。尽管线程同时运行,但程序一次执行一个线程是不寻常的。在我的虚拟机上运行时输出是相同的。发生这种情况的任何可能原因?

代码:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <pthread.h>

//shared variable
int SharedValue = 0;

//mutex lock
pthread_mutex_t lock;

//barrier
pthread_barrier_t barrier;

//which is an integer pointer to where each thread number is stored
void * SimpleThread(void * which) 
    int num, val;
    int thread_num = *(int *) which;

    for(num = 0; num < 20; num++) 
        #ifdef PTHREAD_SYNC
        pthread_mutex_lock(&lock);
        #endif 
        val = SharedValue;

        printf("*** thread %d sees value %d\n", thread_num, val);
        SharedValue = val + 1;
        #ifdef PTHREAD_SYNC
        pthread_mutex_unlock(&lock);
        #endif 
    
    //barrier here
    #ifdef PTHREAD_SYNC
    pthread_barrier_wait(&barrier);
    #endif

    val = SharedValue;
    printf("Thread %d sees final value %d\n", thread_num, val);



/*** main thread ***/

//given two arguments program_name, threads to make
int main(int argc, char* argv[]) 
    int i, threadsToMake, error;

    //verify the user has given number of arguments required to run
    if(argc != 2) 
        printf("Please provide a number of threads to create.\n");
        return 0;
    

    //Verify arguments are within range - integer
    int argumentLength = strlen(argv[1]);
    for(i = 0; i < argumentLength; i++) 
        if(!isdigit(argv[1][i])) 
            printf("Argument is not a number.\n");
            return 0;
        
    
    //convert argument to integer
    threadsToMake = atoi(argv[1]);

    //Making an array of pthreads
    pthread_t thread_array[threadsToMake];
    //makingan array of thread_nums to pass to function
    int thread_nums[threadsToMake];
    //initialize barrier to the number of threads for program
    pthread_barrier_init(&barrier, NULL, threadsToMake);

    for(i = 0; i < threadsToMake; i++)
        //store the thread numbers
        thread_nums[i] = i;

        //Generate that many threads
        //Send threads to SimpleThread
        error = pthread_create(&thread_array[i], NULL, SimpleThread, (void *) &thread_nums[i]);
        if(error != 0)
            printf("Problem creating thread %d", i);
            return 0;
        
    

    //join all threads
    for(i = 0; i < threadsToMake; i++)
        pthread_join(thread_array[i], NULL);
    

    return 0;

2 个线程的异常输出:

*** thread 0 sees value 0
*** thread 0 sees value 1
*** thread 0 sees value 2
*** thread 0 sees value 3
*** thread 0 sees value 4
*** thread 0 sees value 5
*** thread 0 sees value 6
*** thread 0 sees value 7
*** thread 0 sees value 8
*** thread 0 sees value 9
*** thread 0 sees value 10
*** thread 0 sees value 11
*** thread 0 sees value 12
*** thread 0 sees value 13
*** thread 0 sees value 14
*** thread 0 sees value 15
*** thread 0 sees value 16
*** thread 0 sees value 17
*** thread 0 sees value 18
*** thread 0 sees value 19
*** thread 1 sees value 20
*** thread 1 sees value 21
*** thread 1 sees value 22
*** thread 1 sees value 23
*** thread 1 sees value 24
*** thread 1 sees value 25
*** thread 1 sees value 26
*** thread 1 sees value 27
*** thread 1 sees value 28
*** thread 1 sees value 29
*** thread 1 sees value 30
*** thread 1 sees value 31
*** thread 1 sees value 32
*** thread 1 sees value 33
*** thread 1 sees value 34
*** thread 1 sees value 35
*** thread 1 sees value 36
*** thread 1 sees value 37
*** thread 1 sees value 38
*** thread 1 sees value 39
Thread 1 sees final value 40
Thread 0 sees final value 40

【问题讨论】:

如果将 for-loop 行中的 num &lt; 20 替换为 num &lt; 200000 会发生什么? 【参考方案1】:

你怎么知道它不是多线程的? 它可能是多线程的,但由于某些可能的原因,它可能看起来不像。 首先,因为任务非常小,线程创建需要一些时间 第一个线程甚至可能在第二个初始化完成之前完成。

其次,由线程调度器决定每时每刻应该运行哪个线程,在这里他可能决定先运行一个完成,然后再运行另一个。

如果您真的希望看到它是多线程的,请使用更大的任务,例如计算特定范围内的所有素数或计算大数的除数。

这些是需要更多计算的功能,单线程应用程序与多线程应用程序的运行时之间会有明显的差异(如果你在它们之间划分工作)。

【讨论】:

OP 可以通过在SimpleThread() 的开头添加第二个障碍来缓解其中的一些问题,但总的来说,是的,即使有这个额外的障碍,假设一个明显不同的将观察到输出。话虽如此,但是 OP 代码的细节使其特别容易出现他们观察到的问题。 在循环开始之前添加第二个屏障并延长循环持续时间,但仍会产生相同的结果。我真正想了解的是为什么结果被隔离到我的桌面并且无法在其他机器上复制。【参考方案2】:

除了一般注意事项expressed by @Uriel.Gi, 通常,pthreads 互斥锁不会强加公平策略。也就是说,当多个线程竞争获取一个互斥锁时,它不一定会去等待最长时间的那个,最近持有互斥锁的线程也不会被取消优先级。

因此,在最顶部获取互斥锁并在最底部放弃的紧密循环是一种反模式。运行这样一个循环的线程在释放互斥锁后立即尝试重新获取互斥锁,并且它很有可能成功,因为它在释放互斥锁时已经在运行,任何其他等待它的线程都没有,并且尝试重新获取来得如此之快,以至于其他线程没有太多机会介入。这通常会产生这样一种情况,即每个线程都长时间占用互斥锁,而它们之间的切换很少见。

一般来说,这样的问题是设计不佳的症状。临界区不可能同时执行,所以如果基本上所有几个线程的执行都在同一个互斥锁保护的临界区内,那么线程的意义何在?

【讨论】:

【参考方案3】:

您的线程几乎将所有时间都用于打印到同一个输出流。由于一次只有一个线程可以访问该流,因此它们无法在任何重要的并发下运行。

尝试使用多个内核是没有意义的。只有一个内核可以运行代码来访问输出流,而其他内核将等待轮到它们。这非常低效,所以这段代码或多或少只能在一个内核上运行。

鉴于此,让一个线程尽可能长时间地使用内核是有意义的。上下文切换需要时间并使缓存变冷。所以这段代码会让一个核心运行一个线程,直到线程的时间片用完。

所以你得到了我所期望的行为。

【讨论】:

以上是关于计算机不会同时执行多线程程序的主要内容,如果未能解决你的问题,请参考以下文章

JAVA多线程学习

多线程

多线程是啥

多线程

Java线程

Java并发/多线程指南