一个包子引发的血案——不加锁的执行一个简单函数

今天在nowcoder的每日一题上看到这么一个问题,说是人人网的题,原题连接:http://www.nowcoder.com/discuss/1968?type=&order=0&pos=5&page=0

对于下面这个函数,

int add(int *x,int *y,int *z)
{
    *x += *x;
    *y += *x;
    *z += *y;
    return *z;
}
问下面哪个是不可能的返回值

A 4
B 5
C 6
D 7

一眼看过去,就可能看出结果等于4,但由于是笔试题,应该不会这么简单,于是想到要考虑并发的情况,返回4的情况是单个线程顺序执行完所有语句的结果,但如果有多个线程的话会是怎样的呢,通过观察可以发现,返回值等于5、6、7都有可能:

结果是6容易理解:
(1)线程1执行完*x += *x,*x现在变为2
(2)线程1被打断,线程2进入,执行完*x += *x,*x现在变为4
(3)线程2被打断,线程1执行完*y += *x,*z += *y,即得到*z = 6了

注意到右结合操作符+=并非原子操作,结果为5也是可能的:
(1)线程1执行*x += *x,先取右操作数*x,值为1,当取左操作数时被打断,线程2进入
(2)线程2执行完*x += *x,由于此时*x仍然为1,所以得到*x = 2,此时线程2也被打断,线程1继续执行
(3)线程1的+=操作取左操作数*x,值已经是2了,从而写回*x后,*x变为3,接着线程1执行完第二条和第三条语句,就得到*z = 5了

实际上,返回值等于7也是可能的:
(1)线程1执行*x += *x,取右操作数*x,值为1,然后被打断,线程2进入
(2)线程2执行*x += *x,取右操作数*x,值为1,然后被打断,线程1继续
(3)线程1执行完*x += *x,取左操作数*x,值为1,加上1写回*x,*x变为2,然后被打断,线程3进入
(4)线程3执行完*x += *x,此时*x变为4,然后被打断,线程2继续
(5)线程2取左操作数*x,值为4,加上1写回*x,*x变为5,然后被打断,线程1继续
(6)线程1执行完*y += *x,*y变为6,执行完*z += *y,*z变为7


如此看来,在并发数不限定的情况下,*z等于任何大于等于4的值都有可能。如果进一步考虑,限定并发执行的线程数为n,返回值有多少种情况呢?

最小值自然还是4,那最大值呢,如果像上面返回值等于7的情况那样执行,显然有些浪费了,不难想到,如果n个线程先依次执行完*x += *x,在依次执行完*y += *x,最后依次执行完*z += *y,可以得到最大的结果,此时*z = 1 + n * *y =  1 + n * (1 + n * *x) = 1 + n * (1 + n * 2^n)

那在[4, 1+n+n^2*2^n]之间的每一个值都有可能取到吗?

先考虑线程1在执行*z += *y的时候,右操作数*y的取值,我们有*y = 1 + a1*2^1 + a2*2^2 + ... + an * 2^n,其中1 <= a1 + a2 + ... + an <= n

所以*y的取值范围是[3, 1+n*2^n],那每一个值*y都能取到吗?显然不是,因为从上面的表达式来看,*y必然是个奇数,那是不是区间内所有的奇数都能取到呢?

假设p能取到,p = 1 + p1*2^1 + p2*2^2 + ... + pn * 2^n, 1 <= p1 + p2 + ... + pn <= n

q = p+2 = 1 + (p1+1)*2^1 + p2*2^2 + ... + pn * 2^n,如果p1+1 + p2 + ... + pn <= n,则肯定可以,q1 = p1+1, qi = pi

如果q1 + q2 + ... + qn = n+1呢,有抽屉原理我们知道,必然有一个2^k,其系数大于等于2,我们便可以将qk = qk-2,在让qk+1 = qk+1-1,这样就又满足a1 + a2 + ... + an <= n了,因此*y的范围是[3, 1+n*2^n]内的所有奇数

从而*z = 1 + co1 * (2*y1+1) + co2 * (2*y2+1) + ... +con * (2*yn+1),其中1 <= yi <= n*2^n-1,且1 <= co1 + co2 + ... + con <= n

晕了,分析不下去了。。。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值