Pthread意外输出,但效果良好

问题描述 投票:1回答:2

我对c上的pthreads并不熟悉,所以我只是尝试用两个线程来做一些基本程序,这些线程将一个整数递增直到它等于10000,然后每个线程都写出它递增该整数多少时间,并且委托人线程写入最终结果,在我的输出上最终结果很好(总是等于10000),但是每个线程的增量时间是错误的,有人可以向我解释为什么会这样吗?

我的代码:

//Threads in C

/* Includes */
#include <unistd.h>     /* Symbolic Constants */
#include <sys/types.h>  /* Primitive System Data Types */ 
#include <errno.h>      /* Errors */
#include <stdio.h>      /* Input/Output */
#include <stdlib.h>     /* General Utilities */
#include <pthread.h>    /* POSIX Threads */
#include <string.h>     /* String handling */

void *T1 (void * par){
int * cp = (int*)(par);
    printf("Thread 1 begin, counter equals %d \n",*cp);

    int f=0; 
    while((*cp)< 10000) {++(*cp);
                        f++;}
printf("Thread 1 finished, i've incremented %d times \n",f);
pthread_exit(NULL);
}
void *T2 (void * par){
int * cp = (int*)(par);
        printf("Thread 2 begin, counter equals %d \n",*cp);

    int j=0; 
    while((*cp)< 10000) {++(*cp);
                        j++;}
printf("Thread 2 finished, i've incremented %d times \n",j);
pthread_exit(NULL);
}

int main(){
pthread_t idT1, idT2;
int counter = 0;
if (pthread_create(&idT1, NULL, T1, &counter) != 0)
printf("erreur creation");
if (pthread_create(&idT2, NULL, T2, &counter) != 0)
printf("erreur creation");
pthread_join(idT1, NULL);

pthread_join(idT2, NULL);
printf(" Total = %d",counter);
return 0;
}

示例输出:

Thread 1 begin, counter equals 0 
Thread 2 begin, counter equals 0 
Thread 1 finished, i've incremented 10000 times 
Thread 2 finished, i've incremented 8602 times 
 Total = 10000
c pthreads thread-synchronization
2个回答
1
投票

当同时使用2个线程时,可以暂停一个线程,内核可以随时将上下文切换到另一个线程,我们无法预测2个线程之间的代码执行顺序。为简单起见,我将向您展示单核CPU可能发生的情况。

在x86中,计数器通过以下一些指令增加:++(* cp);

movl    (0x80123468), %ecx
addl    $1, %ecx
movl    %ecx, (0x80123468)

&counter =(0x80123468)

想象线程1输入此代码段,计数器将增加1。第一条指令是将计数器的当前值(假设为200)加载到寄存器eax中。因此,线程1的eax = 200。然后第二条指令将一个添加到寄存器;第二条指令将一个添加到寄存器。因此eax = 201。

现在发生了一些事情,使内核停止了线程1的操作(例如,中断),然后将上下文切换到线程2来运行,并且它输入相同的代码。执行第一条指令,加载计数器的值并将其放入自己的eax(每个线程都有自己的寄存器),Thread2-eax = 200,然后执行第二条指令,Thread2-eax =201。然后执行第三条指令以将值201存储到&counter(0x80123468)。

[之后,发生上下文切换,线程2暂停,线程1再次运行,它继续执行第3条指令,并将线程1-eax值= 201存储到&counter(0x80123468)中。

您可以看到,在这种情况下,计数器仅增加1,而j和f都增加1(最初,我们希望其中只有一个增加一个,而不是两个都增加)如果counter = 10000,则while循环会中断,因此该计数器可能是正确的(并非总是如此,我看到的情况可能是10001,但是似乎很少发生,您可以通过检查((*cp)< 10000)的汇编代码来自己弄清楚) ,计数器增加10000次。但是我们不知道j和f增加多少时间?


0
投票

您的程序具有数据争用,因为您的两个线程在不同步的情况下访问相同的共享变量,并且某些访问是写操作。因此,该程序的行为是不确定的。您需要通过适当使用互斥锁,信号量或其他有效的同步机制来保护每个线程对共享计数器的访问(包括读取和写入)。另外,您可以使用原子类型的计数器。

© www.soinside.com 2019 - 2024. All rights reserved.