pthread竞争条件,可疑行为 [英] pthread race condition, suspicious behaviour

查看:97
本文介绍了pthread竞争条件,可疑行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了下面的代码来演示同一进程的2个线程之间的竞争状态.

I have written the follwing code to demonstrate race condition between 2 threads of a same process.

`

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

int c = 0;
void *fnC()
{
    int i;
    for(i=0;i<10;i++)
    {   
        c++;
        printf(" %d", c); 
    }   
}


int main()
{
    int rt1, rt2;
    pthread_t t1, t2; 
    /* Create two threads */
    if( (rt1=pthread_create( &t1, NULL, &fnC, NULL)) )
        printf("Thread creation failed: %d\n", rt1);
    if( (rt2=pthread_create( &t2, NULL, &fnC, NULL)) )
        printf("Thread creation failed: %d\n", rt2);
    /* Wait for both threads to finish */
    pthread_join( t1, NULL);
    pthread_join( t2, NULL);
    printf ("\n");
    return 0;

}

`

我运行了该程序,并期望在2个线程之间发生竞争状态(但是,我知道竞争条件的可能性非常小,因为线程的主要功能非常小). 我跑了50000次.以下是输出,

I ran this program, and expected a race condition to occur between the 2 threads (but, I understand that the probablity for race condition is very less, as the thread main function very small). I ran this 50000 times. The following was the output,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - 49657 times (no race condition)
1 3 4 5 6 7 8 9 10 11 2 12 13 14 15 16 17 18 19 20 - 244 times (race condition occurs)
2 3 4 5 6 7 8 9 10 11 1 12 13 14 15 16 17 18 19 20 - 99 times (race condition occurs)

问题是, 当在输出2中出现争用条件时,线程1打印1并被交换出处理器,线程2进入.它开始工作,线程2打印11后被换出,线程1进入.必须打印12,而是打印2(实际上应该缺少2).我不知道怎么办.请帮助我了解这里发生的情况.

The question is, When race condition occurs as in output 2, thread 1 prints 1 and is swapped out of the processor and thread 2 comes in. It starts working and after thread 2 prints 11, it gets swapped out, thread 1 comes in. It has to print 12, but rather it is printing 2 (actually 2 should be missing). I can't figure out how. Please help me understand what happens here.

推荐答案

您正在考虑采用C语言的思维方式,但是如果您想考虑种族条件,就必须在较低的层次上进行思考.

You're thinking in a C mindset, but if you want to think about race conditions you have to think on a lower level.

在调试器中,通常在一行代码上设置一个断点,并且可以通过单步执行程序来观察正在执行的每一行代码.但这不是机器的工作方式,机器可能会对每行代码执行几条指令,并且线程可以在任何地方中断.

In a debugger, you normally set a breakpoint on a single line of code, and you can watch each line of code being executed by stepping through your program. But that's not how the machine works, the machine may execute several instructions for each line of code and threads can be interrupted anywhere.

让我们看看这一行.

printf(" %d", c);

在机器代码中,它看起来像这样:

In machine code, it looks something like this:

load pointer to " %d" string constant
load value of c global
# <- thread might get interrupted here
call printf

因此行为并非意外.您必须先加载c的值,然后才能调用printf,因此,如果线程被中断,则总是可能会在printf执行任何操作时使c失效. .除非您采取措施阻止它.

So the behavior is not unexpected. You have to load the value of c before you can call printf, so if the thread is interrupted there is always a chance that c is stale by the time printf does anything. Unless you do something to stop it.

解决比赛条件:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int c = 0;
void *func(void *param)
{
    int i;
    for (i=0; i<10; i++) {
        pthread_mutex_lock(&mutex);
        c++;
        printf(" %d", c);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

volatile的作用是什么?

What does volatile do?

问题中的代码可以转换为汇编代码,如下所示:

The code in the question can translate to assembly code like the following:

load the current value of c
add 1 to it
store it in c
call printf

在递增c之后不必重新加载,因为C编译器被允许假定除当前线程外,其他任何人(没有其他线程或设备)都不会更改内存.

It doesn't have to reload c after it increments it, since the C compiler is allowed to assume that nobody else (no other thread or device) changes memory except the current thread.

如果使用volatile,则编译器将严格保留每次加载和存储操作,并且程序集将如下所示:

If you use volatile, the compiler will be strict about keeping every load and store operation, and the assembly will look something like this:

load the current value of c
add 1 to it
store it in c
# compiler is not allowed to cache c
load the current value of c
call printf

这没有帮助.实际上,volatile几乎无济于事.大多数C程序员都不了解volatile,对于编写多线程代码几乎没有用.对于编写信号处理程序,内存映射的IO(设备驱动程序/嵌入式编程)很有用,对于正确使用setjmp/longjmp也很有用.

This doesn't help. In fact, volatile almost never helps. Most C programmers don't understand volatile, and it's almost useless for writing multithreaded code. It's useful for writing signal handlers, memory-mapped IO (device drivers / embedded programming), and it's useful for correctly using setjmp/longjmp.

脚注:

编译器无法在对printf的调用中缓存c的值,因为据编译器所知,printf可以更改c(毕竟c是全局变量).有一天,编译器可能会变得更复杂,并且可能知道printf不会更改c,因此程序可能会更严重地中断.

The compiler can't cache the value of c across a call to printf, because as far as the compiler knows, printf can change c (c is a global variable, after all). Someday, the compiler may get more sophisticated and it may know that printf doesn't change c, so the program may break even more severly.

这篇关于pthread竞争条件,可疑行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆