Javascript 的运行及浏览器多线程

Javascript 的运行及浏览器多线程

2006.12     lu_yi_ming(_at_)sina.com



_ 本文目的

  网页HTML结构以及 Javascript 程序越来越复杂了,有必要整理一下思路。

  本文只是想整理一下思路,很多细节不一定准确。


_ 问题详述

  现在一个网页中包含了多个 Iframe(Frame),每个 Iframe 都有 window (本文中 window 特指 DOM 中的 window,MS Windows 中的窗口与 X Window 中的窗口用 WINDOW 来表示),每个 window 都可以有 setInterval,以及 setTimeout,另外 window.document 里面还有很多的 element,每个 element 都可以有 event,而且这些 event 可以一直 bubble 到 body。

  问题 1. 在一个 window 内部,event 与 window.setInterval / window.setTimeout 是否并行?
  问题 2. window 之间的 Javascript 是否并行?
  问题 3. 属于同一个进程的 WINDOW 之间的 Javascript 是否并行?
  问题 4. 如果有并行存在,是一种怎么样的并行? 是浏览器为每一个 window 或 WINDOW 模拟了多线程,还是利用操作系统本身的多线程?
  问题 5. 如果有并行存在,如何解决 Javascript 运行中的互斥、信号?


_ 初步认识

  经过一些简单研究(因为懒,没去读 Firefox 源代码),有以下初步认识。

  对于 IE,用 SPY++ 分析,发现增加页面中的 Iframe 并不能导致线程的增加,也不能导致子 WINDOW 的增加。所以,IE 中的 Iframe 及 window 是 Shell.Explorer (shdocvw.dll 中的 COM 组件) 内部实现的,可能在一个单独的线程中,很可能就在 WINDOW 所属的线程中。进一步,WINDOW 中所有 Iframe 的 window 的内部的 Javascript 都是不并行的。再进一步,结合网络传输,如果一个 Iframe.src 赋值后,Javascript 所在的线程应该会通知另一个线程(本文中简称通讯线程)去向服务器发 HTTP 请求。通讯线程从服务器端接收了一个完整的 HTTP 包后,将把这个数据包交给负责显示的线程(中间环节可能有一个线程负责解析 html),显示线程(或解析线程)边显示边运行页面中的 Javascript,例如 document.write()。并不停的绘制屏幕。而在这个 HTTP 数据包处理过程中,并不会处理其他 Iframe 的 window 的 setInterval 之类的事件。处理完一件事情后,才会在内部的 timer / timeout 队列中选择一个到期的开始运行(调用)。

  所以,如果有一个 Iframe 的 window 的 Javascript 陷入死循环,则此线程内的所有的 Iframe 都将停止响应,并且从服务器端接收来的新数据也将被搁置。

  IE 中的多线程。在 IE 上按 Ctrl-N,或者运行 window.open() ,将会打开一个新的 WINDOW,从 SPY++ 中看,会相应的增加一个新线程,管理这个新的 WINDOW。但是没有增加更多的新线程,说明一个进程内所有的 WINDOW 都共用了通讯线程。也就是通讯进程管理了所有的 TCP Connection。 用 window.open() 返回的对象指针可以访问这个新 WINDWO,并且可以访问其下的 Iframe 的 window 中的各种数据,甚至也可以调用其中的函数,只是需要注意堆栈的不同。经过测试,如果两个线程的 Javascript 同时访问同一个数据,比如同时有 window.setInterval 指定的函数去访问一个 window 中的一个 element 的变量,或者修改属性,则其中的一个函数会返回 Javascript Exception:拒绝访问,说明浏览器在内部实现中注意了互斥。

  基于以上认识,对于 IE ,在一个 WINDOW 的所有 Iframe 的 window 内部应该不存在多线程,也不存在 Javascript 并行。当然也不会有 sleep 之类的函数了。把一个 Javascript 函数阻塞了(如特定的 ActiveX )可能导致整个 WINDOW 暂停相应。

  关于 IE 还有一个发现,window.setInterval 不是用 WM_TIMER 实现的。

  关于 Firefox,在 Windows 上,用 SPY++ 分析,页面中的每一个 Iframe 都是用一个 WINDOW 实现的。但是所有的 WINDOW,包括其他 Tab 打开的 WINDOW,都属于一个线程。但是跨 Tab ,跨浏览器窗口访问同时访问一个数据,也会引发 Javascript Exception:拒绝访问,说明 Firefox 的 Javascript 很可能是在一个 Tab 范围内,是不并行的。有可能是 Firefox 给每个 Tab 单独开了一个线程来运行 Javascript。反正最终行为要与 IE 保持差不多,估计也就这样了。

  在 Linux(FC6)上启动 Firefox,用 gdb attach 进去,发现在新打开网页时会增加不少线程,但网页显示稳定后过一段时间,线程数会减少。这也可能是因为运行 Javascript 的线程任务做完了。


_ 初步结论

  1. 在 IE 的一个 WINDOW 内,Firefox 的一个 Tab 内, Javascript 是单线程运行的。
  2. 如果要在页面上的各 Iframe 之间实现 Javascript 同步,则应该采用 window.setInterval 来实现,而不是把一个 Javascript 挂起。
  3. 在 IE 上跨 WINDOW,或在 Firefox 上跨 Tab 访问数据,则要注意用 try catch 解决线程互斥导致的 Exception。

你可能感兴趣的:(其他)