首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

探索 XUL 内的多线程编程-2 测试

探索 XUL 内的多线程编程-2 测试

口味测试应用程序如前面所提到的,多线程的一个常见应用就是某类 IO 操作,例如访问 Internet 上的一个 Web 服务。现在 JavaScript/XUL 总是可以为网络操作提供多线程:XMLHttpRequest。Ajax 的秘密武器就是在后台线程上执行网络 IO,而调用程序则提供一个 callback 函数,该函数在 IO 完成后在主线程上调用。不过,您对这个线程没有显式的控制;运行时负责这一点。此外,如果需要做多个网络操作,又该怎么办呢?在这种情况下,请求将被运行时序列化。在本应用程序中,我们将需要执行这一类任务 — 通过 Workers API 用多个线程同时访问多个远程 Web 服务。当用户查看并比较 Google、Yahoo 或 Microsoft Bing 这三个搜索引擎的匿名结果时,此应用程序将允许用户执行一个 “蒙目(blind taste)的口味测试”。
三个搜索引擎,三个线程下面将要探讨的这个应用程序非常简单。此应用程序允许用户输入一个或多个搜索条件。随后它将搜索这三个搜索引擎,并且匿名地显示搜索结果,因此用户并不知道哪个结果列表出自于哪个搜索引擎。我们从用户界面的 XUL 代码开始(参见 )。
清单 1. XUL UI 代码
1
2
3
4
5
<hbox>
    <label value="Enter" id="lbl"/>
    <textbox value="" id="term"/>
    <button label="Go!" onclick="search()" id="btn"/>
</hbox>




这是一段简单的 XUL 代码,可创建一个搜索表单。它用三个小部件创建了一个水平布局。第一个小部件是一个标签,第二个小部件是用户可在其中输入搜索条件的一个文本框,第三个小部件是一个按钮。当用户单击这个按钮时,就会调用搜索函数。这也是派生 worker 线程的地方,如  所示。
清单 2. 派生 Workers
1
2
3
4
5
6
7
8
9
10
11
12
function search(){
    var keyword = document.getElementById("term").value;
    var workerScripts = ["google.js", "yahoo.js", "bing.js"];
    var worker = {};
    for (var i=0;i<workerScripts.length;i++){
        worker = new Worker(workerScripts);
        worker.onmessage = function(event) {
            displayResults(event.data);
        };
        worker.postMessage(keyword);
    }
}




如果您使用的是基于 JavaScript 1.9.1 以前版本的 XUL,那么就需要对所有这三个请求进行排队,或是使用 XPCOM 与 C++ 来有效地检索这些数据。有了 JavaScript Workers,派生多线程来检索该数据就变得十分简单。每个 Worker 对象都为其构造函数获取了一个单一字符串。这个字符串是另一个 JavaScript 文件的所在位置,该文件将成为这个 worker 的主体。worker 文件中的脚本将在其自已的线程上执行。
所谈论的这三个脚本被分别命名为 google.js、yahoo.js 和 bing.js。将它们放入一个数组,然后对该数组进行迭代。对于这个数组中的每个脚本,只需传递进指定 worker 脚本位置的那个字符串就可以创建 Worker。接下来,针对每个所创建的 worker,设置其 onmessage 函数。这个函数将在 worker 向主线程返回数据时被调用。在本例中,将此工作简单地委托给另一个名为 displayResults 的函数就可以了。最后,要发送想要用于各搜索引擎的搜索条件的名称,需要在每个 Worker 对象上调用 postMessages 函数。这将使此 worker 线程得以访问 Web 服务,进而,就可以并发调用这三个 Web 服务。不仅调用是同时的,而且结果的处理也是并行完成的。
那么这个 onmessage/postMessage 范型到底说明了什么?这是 Workers API 背后的基础线程模型。为大多数开发人员所熟知的常用线程模型是被 C++、 Java™ 技术及其他编程语言所使用的那个模型。为了通信,线程通常会修改所共享的内存。这很有效,但会带来很多问题(信号量、互斥等)。对于 UI 编程来说,也会遇到同样的问题,因为如果用多个线程更改 UI,就可能会导致 UI 锁死或崩溃。为了避免将所有这些复杂性带入 XUL,JavaScript Workers API 使用了一个更简单的模型。这个模型建立在消息传递的基础上,并且与 Erlang 和 Scala 中的 actor 模型有点类似。
让我们返回到 ,Worker 实例充当所派生线程的一个代理。要向线程发送一个消息,需要调用 Worker 实例上的 postMessage 方法。任何对象都可以被传递到这个方法。当然,这个派生的线程也可以将消息传递回其母线程。在向母线程传递消息时,会调用 Worker 实例的 onmessage 方法。这是一个不可选的默认实现,因此必须要覆盖它(如果您对从这个线程返回的结果感兴趣的话)。在  中,要覆盖它,需要将它设置为等同于能接受单一参数的函数,名为 event。这是被从派生线程发送来的对象。对于  中的这个函数,提取 event 的 data 属性,因为这是被线程发送的实际数据,然后将它发送给另一个函数来更新 UI。
您已经对母线程有了一点了解。那么对于派生或子线程您又了解了多少呢?如前面  所示,对于每个派生线程,都要使用单独一个 JavaScript 文件。在  中,我们先来看看访问 Google 搜索 API 的那个线程。
清单 3. Google 搜索 worker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
onmessage = function(event){
    var keyword = event.data;
    var results = searchGoogle(keyword);
    postMessage(results);
}

function searchGoogle(keyword){
    var url = "http://ajax.googleapis.com/ajax/services/"+
        "search/web?v=1.0&rsz=large&q="+keyword;
    var xhr = new XMLHttpRequest();
    xhr.open("GET", this.url, false);
    xhr.send();
    var response = JSON.parse(xhr.responseText);
    var results = [];
    var result = {};
    var data = response.results;
    for (var i=0;i<data.length;i++){
        result.url = data.url;
        result.title = data.title;
        result.description = data.content;
        results.push(result);      
    }
    return results;
}




中的代码与  中的代码有些相似。这个线程有一个 onmessage 方法。它就是每当消息被发送给线程时就会调用的函数。如果回到清单 2,那里调用的是 Worker 实例上的 postMessage 方法。这将导致清单 3 中的 onmessage 的函数的调用。这样一来,传递给 postMessage 的这个对象就成为了传递给 onmessage 方法的那个对象的数据属性。它是本示例中搜索用的关键字。
返回列表