没有合适的资源?快使用搜索试试~ 我知道了~
白话并发冲突与线程同步[整理].pdf
1.该资源内容由用户上传,如若侵权请联系客服进行举报
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
版权申诉
0 下载量 72 浏览量
2021-10-12
05:27:36
上传
评论
收藏 1.97MB PDF 举报
温馨提示
试读
17页
白话并发冲突与线程同步[整理].pdf
资源推荐
资源详情
资源评论
猴子抬头道: “我有一个梦,我想我飞起时,那天也让开路,我入海时,水也分成两边,众仙诸神,见我也称兄弟,无忧无虑,天下再无可拘我之物,再无可管我
之人,再无我到不了之处,再无我做不成之事,再无我战不胜之物。”
—— 今何在
摘自《悟空传》
摘要
男程序员勿进。(因为可能女程序员拍砖的力道会小些,俺比较能扛得住 ……)
并发冲突 ——当一条虫子遇上两只小鸡会发生什么事情?
当一条虫子遇上两只小鸡会发生什么事情?
可以肯定的是,那条虫子必定会去见上帝啦。
无法确定的是,到底是那虫子的上半截先去见上帝,还是下半截先去见上帝?
你一准儿在想: “我昨天晚上加班到 12 点,到现在还晕乎乎的。本想到博客园逛逛,可以暂时忘掉那些复杂多变的需求、防不胜防的 Bug 以及让人迷惑的办公室政治,
没想到却遇到了个精神病,在这儿琢磨这种无聊问题。 ”
相信我,这绝对是性命攸关的重要问题!想想看,如果是虫子的下半截先去见上帝,上帝一准儿会问它: “你叫啥?是怎么死的啊? ”,虫子的下半截答道: “我叫虫子甲,
是被小鸡乙吃掉的。 ”于是上帝在他的本子上写到: “虫子甲是被小鸡乙吃掉的。 ”然后过了一会儿,虫子的上半截也见到了上帝。上帝也问它: “你叫啥?是怎么死的啊? ”
虫子的上半截答道: “我叫虫子甲,是被小鸡甲吃掉的。 ”上帝打开他的本子,发现里面已经写了 “虫子甲是被小鸡乙吃掉的。 ”,然而此时虫子的下半截已经不知爬到哪里
逍遥去了,无法详细盘问,于是上帝只好选择相信虫子的上半截,把本子上的记录改成 “虫子甲是被小鸡甲吃掉的。 ”
但是如果碰巧是虫子的上半截先去见的上帝,最后在上帝的本子上就会写着 “虫子甲是被小鸡乙吃掉的 ”。
也就是说,这条可怜的虫虫的死最后会算在哪只小鸡的头上,完全是不可预测的!这个问题不但让上帝颇为头痛,也有可能让程序员丢了性命,欲知后事如何,一段广告
过后,马上回来!
广告音: “博客园,不是白菜园、不是幼儿园、不是游乐园,更加不是罗卜家园 ……博客园,程序员的网上家园。 ”
1-2-3 的超级程序
我最近给客户开发了一个非常厉害的程序。
class Program
{
static int n = 0 ;
static void foo1()
{
for ( int i = 0 ; i < 1000000000 ; i++) // 10 亿
{
int a = n;
n = a + 1 ;
}
Console.WriteLine( "foo1() complete n = {0}" , n);
}
static void foo2()
{
for ( int j = 0; j < 1000000000 ; j++) // 10 亿
{
int a = n;
n = a + 1 ;
}
Console.WriteLine( "foo2() complete n = {0}" , n);
}
static void Main( string [] args)
{
foo1();
foo2();
}
}
怎么样?只用了 40 秒钟,这个程序就计算出了把一个初始为 0 的变量 n 累加 20 亿次 1,变量 n 将等于 20 亿。什么?你说我是白费 CPU 不干正经事?这有什么,客
户喜欢!顺便说一句,我的电脑是 8 年前买的,用的是赛扬 800 的 CPU 。
能更快些么?
可是,客户居然嫌它太慢了,并且威胁说如果不能把它压缩到 10 秒以内就让我去见上帝。
我的客户怎么这么狠?唉,打从一开始我就觉得这个总喜欢 “击地 ”的山羊胡老头有些眼熟,这下后悔也晚了,用多线程试试吧。
使用多线程
现在知道我的程序为啥要使用两个函数 “foo1() ”和“foo2() ”来实现了吧?因为我早就算到了这个情况,为使用多线程做了准备,现在我只要把 “foo1() ”和“foo2() ”分别
用两个线程来执行就可以了。(要不怎么说再好的架构师也比不上一个能掐会算的算命先生呢?)
class Program
{
static int n = 0 ;
static void foo1()
{
for ( int i = 0 ; i < 1000000000 ; i++) // 10 亿
{
int a = n;
n = a + 1 ;
}
Console.WriteLine( "foo1() complete n = {0}" , n);
}
static void foo2()
{
for ( int j = 0; j < 1000000000 ; j++) // 10 亿
{
int a = n;
n = a + 1 ;
}
Console.WriteLine( "foo2() complete n = {0}" , n);
}
static void Main( string [] args)
{
new Thread(foo1).Start();
new Thread(foo2).Start();
}
}
你说奇怪不奇怪?一下子结果全都不对了,而且每次执行的结果都不一样!在责怪 CPU 有 Bug 、内存有毛病、操作系统中了病毒之前,不妨先来分析一下这段代码是如
何执行的。
附言:用了多线程之后,程序执行时间是 34 秒,所以就算结果正确我的小命也一样不保。
把 n 加 1,统共分 3 步
在上面那个经过特别设计的例子中,把 n 加 1 ,统共分 3 步:
第一步,把 n 的值保存到 a 中。
第二步,计算 a+1 的值。
第三步,把 a+1 的结果保存到 n 中。
如果你不能确定上面所说的这三步是不是事实, 可以看程序的汇编代码 (方法是先在 VS2005 里单步执行, 然后使用菜单 “调试 > 窗口 > 反汇编 Ctrl+Alt+D ” 打开反
汇编窗口)。下图截取了汇编代码并使用了相应的伪码,涂了不同的底色以备后用。
正如我们所知道的, CPU 只有一个,所以所谓的多个线程 “并发执行 ”只不过是把这些线程排好队,然后让他们一个挨着一个地轮流使用 CPU ,每个线程只使用很短很短
的时间。这样对于人类这样反应迟钝的动物来说,就感觉好像有多个线程在 “同时 ”执行。让我们来看看,当第一个线程执行完 “把 n 的值保存到 a 中”时,时间到!该轮到
别的线程执行了, 这时会发生什么事情?这时, 你会听到 Windows 大喝一声: “帮我照顾好我七舅姥爷和他三外甥女 ——”,然后咔嚓一下就把第一个线程暂停了。 所以,
如果我们的第一、第二个线程的前三次循环以下图所示的顺序来执行是一点也不奇怪的。(黄色底色的代码属于第一个线程,绿色底色的代码属于第二个线程)
现在,第一、第二个线程里面的循环各自执行了 3 次, n 的值是 3 ,而不是我们期望的 6。所以,即使我们的电脑里只有一个 CPU(还不是双核的),一样会遇到 并发冲
突的问题。
真有人在 Windows 里群殴?
听说有线程发生了并发 “冲突 ”,我们都睁大了眼睛,可实际上并没有什么热闹好看 —— 那两个线程并未打得不可开交。它们虽然访问了同一个全局变量 n ,但是并未给对
方或自己造成什么伤害。我们说这两个线程发生了并发冲突,其实想表达的意思不过是 “它们做了我们我们不希望看到的重复工作 ”而已。
好吧,为了活命,我们必须找到防止这两个线程做重复工作 —— 也就是线程同步 —— 的方法。不过在此之前,先来看看在什么情况下不需要操心线程同步的问题。
不需要线程同步的情况
1. 对 n 的读取、赋值操作用一条汇编语句就能搞定的时候。
我们把程序稍稍改动一下:
如您所见, “n=n+1” 所对应的汇编代码只有一行 “inc dword ptr ds:[01608A60h] ” ,也就是通过 inc 指令直接把 CPU 的 cache 里的 n 的值增加 1 。 CPU 的 catch
真是一个方便的发明呀。不过现在高兴还有些早,因为我们还没有考虑多 CPU 的情况。要知道现在的服务器大多具有 2 个以上的 CPU ,就连 PC 机都是双核(一块芯片
里含有 2 个逻辑 CPU 并且有两个 cache ,就跟安装了两块 CPU 没啥两样)的了。由于 CPU 在把 cache 里的 n 增加 1 之后,并不会立即把 n 的值写入到内存中,所以
如果我们在安装了 2 块 CPU 的计算机上执行上面那段程序,并且假设第一个线程由 CPU1 来执行,第二个线程由 CPU2 来执行,那么这两个线程的前 3 次循环完全有
可能像下面这样:
非常不幸地, n 的值是 3 而不是我们期望的 6 。CPU cache 这个方便的发明现在成了烫手的山芋。 不过如果你在装有 Intel 的双核 CPU 的计算机上运行上面的代码,会
发现结果仍然非常正确,似乎上图所示的麻烦事并没有发生,这是为什么呢?这是因为 x86 架构的 CPU 非常地道,它在确保 cache 一致性方面做了很多努力。
====== 2008-5-26 更新 ======
剩余16页未读,继续阅读
资源评论
czq131452007
- 粉丝: 2
- 资源: 12万+
下载权益
C知道特权
VIP文章
课程特权
开通VIP
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功