首页 > 试题广场 >

两个等价线程并发的执行下列程序,a为全局变量,初始为0,假设

[单选题]
两个等价线程并发的执行下列程序,a为全局变量,初始为0,假设printf、++、--操作都是原子性的,则输出肯定不是哪个?
void foo() {
    if(a <= 0) {
        a++;
    }
    else {
        a--;
    }
    printf("%d", a);
}
  • 01
  • 10
  • 12
  • 22
推荐
每个线程进foo函数不止一次,那么我们暂且假设两个线程分别进入foo函数X次,
假设给线程编号,线程1有m次被堵在a++,线程2有n次被堵在a++处,
那么线程1必然会执行(X-m)次a- -,线程2必然会执行(X-n)次a- -,
那么最终a的值为(m+n)-((X-m)+(X-n))=2(m+n)-2X,
那么a必然是偶数,

因此只有答案A是奇数,选择他不用怀疑。
看懂黑体字就是王道
参考:http://www.cnblogs.com/obama/archive/2013/05/06/3061529.html
编辑于 2015-01-27 17:31:49 回复(32)

对于B选项:P1执行程序,输入1,P2执行程序,输出0;

 

对于C选项:初始为0,P1执行完判断语句,决定要执行a++,中断,P2进行判断,此时a仍然等于0,执行判断语句,并执行输入,得到1,P1然后继续执行,此时它该执行a++,这时a=1,执行并输出,结果为2;

 

对于D答案:初始为0,P1执行完判断语句,决定要执行a++,中断,P2进行判断,此时a仍然等于0,执行a++,得到a=1,中断,P1继续执行a++,a=2,P1输出,得到2,P1结束,P2继续执行输出语句,得到2;

发表于 2014-11-18 18:14:51 回复(15)
与a有关的语句有3个:a<=0,判断语句,从内存取值,不写回内存;a++/a--,从内存取值,有对a的赋值,结果要写回内存;printf打印a,要从内存取值。
1、线程P1执行完foo(),线程P2再执行foo()。结果是10。
2、线程P1从内存取出a=0,执行判断语句后等待;线程P2从内存取出a=0,执行判断语句后等待;线程P1再执行a++,a变为1,写回内存,等待;线程P2从内存取出a=1,再执行a++,a变为2,写回内存,等待;线程P1执行printf,输出2,线程P2执行printf,输出2。结果是22。
3、线程P1从内存取出a=0,执行判断语句后等待;线程P2从内存取出a=0,执行判断语句后等待;线程P1再执行a++,a变为1,写回内存,继续执行printf,输出1,等待;线程P2从内存取出a=1,再执行a++,a变为2,写回内存,继续执行printf,输出2。结果是12。
4、还有种可能是00。但不会是01。
发表于 2016-05-16 15:58:50 回复(0)
这关偶数基数什么事,不是2个线程一个线程打印1一个线程打印0么,怎么被看成了10???
发表于 2016-01-09 09:33:26 回复(1)
看到前面的答案都不太满意,我还是想简单的分析下:首先题干说明了等价线程,printf、++、--都是原子性的
  •  最简单无冲突情况,输出一定是10,也就是两个线程执行没有交叉。无所谓那个线程先执行,呈现的结果一定都是10。
  •  然后根据题干,两个冲突地方会发生:  
  1. 判断a<=0时:所谓的冲突情况是指,判断a<=0是真时,a却被别的线程改变成a>0的情况,这种家假设下,在该线程中a的值就变为2。而这里冲突是不可能存在a<=0是假的情况。可以仔细想想这个逻辑
  2. 然后第二个冲突的地方就是if(a<=0)和printf之间,如果这个时候有别的线程对a修改,则会发生两个两个线程输出结果相同的情况。
综上,可能出现的结果10(理想),22(两种冲突都发生),还有12(第一种冲突),00(第二种冲突发生)所以答案已经出来了,但是有没有可能出现01的情况呢?答案是否定的,在两个等价线程执行foo的场景下,第一个输出无论如何冲突(或者叫歧义)都不能导致输出01.
注:有的答案分析++,--,printf的汇编操作,题干都说了是原子操作。还有有的函数是thread-safe的,线程重入并不一定引发歧义。分析的时候只要把握该语句的歧义,及语句间歧义的情况。所有对线程共享数据的访问,无论读和写。都可能引发歧义。单纯的读是不会产生歧义。

编辑于 2018-05-01 17:19:42 回复(0)
1  voidfoo() {
2     if(a <= 0) {
3        
4        a++;
5
6    }
7    else{
8
9        a--;
10
11    }
12    printf("%d", a);
13     }
比较蠢但是直观的方法:

1.线程一从头到尾执行一次,然后线程二从头到尾执行一次; 输出 1,0 ; B
2.线程一执行到第3行的时候(还没有开始执行a++),
失去cpu执行权,线程二拿到cpu执行权,此时也到了第3行,然后线程一执行完毕后,再执行线程二。 这时输出的是1,2; C
3.线程一和线程二依次执行到第3行,此时线程一执行到第五行,a=1,然后线程二执行到第五行a=2,最后输出 2,2;   D

0,1这种,无论什么情况下都是不会发生的
发表于 2017-02-20 15:44:54 回复(2)
无论怎么说“线程等价”,两个线程执行次数都不可能是一样的,或者说绝大多数情况下两个线程执行次数是不相等的 !!!而且,题目假设printf、++、--操作都是原子性。并没说函数foo()是原子性 !!!也就是说foo()在执行时,并不是执行完毕再调用另一个线程,而是可以执行一个原子操作(如++)就中断,然后另一个线程执行。所以A是对的,同样B,C,D也是对的!
发表于 2015-09-19 14:49:32 回复(0)
对于B选项:P1执行程序,输入1,P2执行程序,输出0; 对于C选项:初始为0,P1执行完判断语句,决定要执行a++,中断,P2进行判断,此时a仍然等于0,执行判断语句,并执行输入,得到1,P1然后继续执行,此时它该执行a++,这时a=1,执行并输出,结果为2; 对于D答案:初始为0,P1执行完判断语句,决定要执行a++,中断,P2进行判断,此时a仍然等于0,执行a++,得到a=1,中断,P1继续执行a++,a=2,P1输出,得到2,P1结束,P2继续执行输出语句,得到2;
发表于 2018-02-28 22:24:20 回复(0)
没看到a初始为0。。。如果a初始为-1 A就可以了。
我的理解方法是通过从左上角的操作任意连线到右下角的操作,连线通过所有操作。
(垂直方向不可跨行连,比如不可 增/减前A-输出A)
以下AB只代表区分两个线程操作,实际上是同一个东西。默认增减前A的操作在最先,输出B的操作在最后,对结果不影响。

初始:0
threadA                  threadB
增/减前A                增/减前B                 //即进入判断,自增自减前
增/减后并存入A     增/减后并存入B     //即进行自增、自减并存入,“存入区a”只有一个非0则1
输出A                     输出B                     //输出“存入区a”的数字

输出00,一种连线:
增减前A(0准备++)-增减并存入A(1)--增减前B(1准备--)-增减后并存入B(0)-输出A(0)-输出B(0)

输出10,一种连线:
增减前A(0准备++)-增/减后并存入A (1)-输出A(1)-增减前B(1准备--)-增/减后并存入B(0)-输出B(0)

//从上面两个就可以看出为什么不能连线出输出01,因为如果要“输出(0)”,则需0已存入,则需“增/减后并存入(0)”,只有1(准备--)或-1(准备++)才能做到,而初始时为0。输出00则是因为0++,1--后变为0,其实是重复输出了第二次的结果。

输出12,一种连线:
增减前A(0准备++)-增减前B(0准备++)-增/减后并存入A(1)-输出A(1)-增/减后并存入B(1++=2)-输出B(2)

输出22,一种连线:
增减前A(0准备++)-增减前B(0准备++)-增/减后并存入A(1)-增/减后并存入B(1++=2)-输出A(2)-输出B(2)
发表于 2017-04-05 14:19:26 回复(0)
反对 最高票答案,完全是胡诌误导凑答案。

原子操作指的是这个过程或者直白点说,这句函数不会被中断。因此printf、++、--这三个操作之间排列两个线程就行。

A、如果要第一个值输出0,线程1进入判断并实现++,线程2此时进入判断实现--,a=0,两个线程下一句都是print,结果将是00;(考虑到线程并发和内存抢占的情况,暂不考虑一个线程print0以后还是这个线程执行一遍程序打印出1,而是由另一个线程来print0)
B、线程1判断,执行++,打印1,线程2判断,执行--,打印0,结果10;
C、线程1进入判断,线程2进入判断,线程1执行++,打印1,线程2执行++,打印2,结果12;
D、线程1进入判断,线程2进入判断,线程1执行++,线程2执行++,各自打印出2,结果22。
发表于 2017-02-28 15:14:44 回复(10)
D
发表于 2020-02-20 00:00:32 回复(0)
个人见解:答案没问题 需要注意的是a是全局变量 两个线程不分先后顺序
B:一个线程先先执行完 判断if 输出1,此时a=1 ,线程2开始执行,也是执行完输出0
C:两个线程同时进入 if 语句,一个线程执行a++,输出1,此时a=1并且另一个线程在 if 语句等待,然后另一个线程开始执行a++,输出2
D:两个线程同时进入 if 语句,一个线程执行a++,但不输出此时a=1并且另一个线程开始执行a++,此时a变成2,然后两线程开始输出,并输出2 2
A:假设一个线程进入if 语句,要么和另一个线程同时进入if 语句执行a++,要么在另一个线程判断之前执行a++,照着两种情况,是输出不了 01 的,可以输出0 0
个人觉得只要注意a是全局变量   print语句执行的时机,很好判断^-^.

发表于 2019-09-24 21:56:04 回复(0)
每个线程进foo函数不止一次,那么我们暂且假设两个线程分别进入foo函数X次, 假设给线程编号,线程1有m次被堵在a++,线程2有n次被堵在a++处, 那么线程1必然会执行(X-m)次a- -,线程2必然会执行(X-n)次a- -, 那么最终a的值为(m+n)-((X-m)+(X-n))=2(m+n)-2X, 那么a必然是偶数,
发表于 2019-03-18 18:03:54 回复(0)
选项的顺序为输出顺序,不是P1P2输出
所以先执行完P2再执行P1,输出为10,不是01
还有无法都在执行printf之前中断,然后先输出P1再输出P2,因为a为全局变量

对于B选项:P1执行程序,输入1,P2执行程序,输出0;

 

对于C选项:初始为0,P1执行完判断语句,决定要执行a++,中断,P2进行判断,此时a仍然等于0,执行判断语句,并执行输入,得到1,P1然后继续执行,此时它该执行a++,这时a=1,执行并输出,结果为2;

对于D答案:初始为0,P1执行完判断语句,决定要执行a++,中断,P2进行判断,此时a仍然等于0,执行a++,得到a=1,中断,P1继续执行a++,a=2,P1输出,得到2,P1结束,P2继续执行输出语句,得到2;

发表于 2018-11-18 10:21:43 回复(0)
并发是程序在一个core上交替运行
发表于 2018-08-29 10:09:18 回复(0)

眼镜打蚊子去了

发表于 2018-07-10 20:13:07 回复(0)
推荐答案感觉是错的。这道题的意思,我觉得是两个线程各执行一次打印。选项中的两个数字也就是先后打印的了,那么不可能先打印0,后打印1。这个可以自己思考一下。
发表于 2018-02-19 10:13:33 回复(0)
没有官方解释,评论区总是感觉都在凑答案...每次我想问的都无法在评论区找到答案,向大佬求助小弟这样想有什么问题:
这个题我感觉可以输出01。下面以【x】代表不同线程
【1】读取a,此时a=0;
【1】进行a++,此时a=1;
【2】读取a,此时a=1;
【2】进行a--,此时a=0;
【2】进行printf,输出0;
【1】进行printf,输出1;
结果0 1
发表于 2017-12-20 09:57:45 回复(6)
go8头像 go8
跟奇偶数无关啊,不要带错路了。是两个线程分别打印出来的
发表于 2017-02-27 21:30:56 回复(0)
发表于 2017-02-25 18:33:33 回复(0)
A选项: p1执行完判断语句,此时a = 1,正要执行printf输出时,发生中断,P2整个执行完并输出,再执行p1的输出。结果不是01么?
发表于 2017-01-12 11:31:47 回复(1)