Google

星期一, 三月 31, 2008

[转载]使用AJAX的十大理由(译文)

保守来说,AJAX在现在是热得不能再热的技术。没有人能否认,它拥有大批的支持者。在CNN上,它从二月份的一个不被看好的词语到十月份成长成一个初具 雏形的技术。所以,有必要要看看为什么AJAX能发展成为现在的样子,为什么它能不断成长,并且在短的时间内迅速变得无处不在。所以,我用午夜谈话的风 格,来给出 需要AJAX技术的十大理由。

使用AJAX的十大理由:

10。XAML, XUL, XForms...等等。
9。服务端技术的不确定性。
8。Web2.0。
7。被软件工业领袖们强势采用。
6。和Flex 和 Flash等技术的很好的集成。
5。边际成本低。
4。能使常规的Web应用受益。
3。跨浏览器和跨平台。
2。以可用性和用户体验为王。
1。基于公开标准。

十大理由第一名:公开标准

让 我们从第一条开始,AJAX技术是基于被各大浏览器和平台都支持的公开标准的技术。这意味着该技术不怕技术提供商的技术封锁。组成AJAX技术的大多数技 术都能放心的使用很多年,而那些不是热点的、最新的和未经考验的技术只能使用一段时间。现在,对于绝大多数的用户和企业来说,浏览器是一个可信任的应用平 台,这在五年前就不是个问题了。对于AJAX来说,FIREFOX浏览器的基础Mozilla 1.0的发布并且支持XML HTTP Request对 象是一个转折点。这种允许异步数据交换的技术好多年前就被IE浏览器支持了。这种支持和FIREFOX浏览器的大量被采用真正的使人们理解了跨浏览器的富 Internet应用成为了可能。

1)JavaScript or ECMA Script (Standard ECM A- 262): 一个有趣的事情是,Javascript是经过长时间后才成为被人们接受的技术,长时间以来,很多公司采用非Javascript技术的方针,幸运的是, 这种状况被迅速的改变。
http://www.ecma-international.org/publications/standards/Ecma-262.htm

2)XML:是一个来自W3C的、被广泛应用的标准。
http://www.w3.org/XML/

3)HTML: http://www.w3.org/MarkUp/

4)CSS: http://www.w3.org/Style/CSS/

5)XML HTTP Request Object:被Internet Explorer、Mozilla-based、Safari和Opera浏览器支持。

十大理由第二名:可用性

开 发人员和设计人员开始认识到不仅大型的用户体验在市场上是成功的,而且也认识到这样体验是怎么来影响用户的开销的。基于AJAX技术的google地图比 传统的选择MapQuest更成功,证明了提供更好的用户体验的产品的成功。AJAX技术是使网络应用有更好的可用性的一个领导性的技术。它允许从服务器 端请求少量的信息,而不是整个网页。它增加了页面数据的更新但同时减少了页面的刷新和刷新等待,这些问题从网络已诞生就折磨着Web应用。

人们已经知道他们需要一个优秀的用户界面并且有对该界面的投资意愿。前提条件是:用户能够快速的取得信息不管数据是一个内部网的应用还是一个广域网的服务。

十大理由第三名:跨浏览器和跨平台的兼容性

IE 和基于Mozilla的FIREFOX是占据市场分额最大的两个浏览器,并且它们都支持在浏览器上轻松创建基于AJAX的WEB应用。现在开发运行在更为 先进的WEB浏览器上的基于AJAX的富WEB应用成为了可能。这是为什么AJAX应用变得如此流行的一个最重要的原因。其实很多开发人员多年前就意识到 AJAX技术流行的可能,但一直没有流行是因为浏览器厂商的原因。感谢Mozilla和FIREFOX。

十大理由第四名:使常规的WEB应用受益

AJAX 技术是当今WEB应用的门面——WEB应用获得的利益超过了桌面应用。这些利益包括部署应用的低投入、维护方便、缩短开发时间和不需要安装。这些都是促使 商业和用户自从上世纪九十年代以来采用WEB应用的优点。AJAX技术不但能使WEB应用获得益处,而且使最终用户受益。

十大理由第五名:促使技能、工具和技术的升级

由 于AJAX基于这些年一直使用的一些公开标准,很多的开发人员就会有新的技术方面的要求以便能够开发AJAX应用。但这并不意味着开发团队从基于HTML 和FORM的应用转移到富AJAX型应用需要很高的学习曲线。同时,这意味着开发WEB应用的开发团体需要加速将他们的用户接口升级到AJAX,但并不需 要一个大规模的升级和重写他们的WEB应用。自从上世纪九十年代以来,在开发基于浏览器应用方法花了大量投资的那些系统强烈的希望能在现有的应用的基础上 增加用户体验。

十大理由第六名:能和Flex 和 Flash等技术的很好的集成

大多数的开发社区都不再 支持Flash vs AJAX的火热讨论,这两种技术都在不同的场合拥有各自的优点和缺点,但是它们有大量的机会可以集成到一起工作。很多的开发人员和 技术提供商意识到这一点,并且开发出了伟大的产品来集成Flex和AJAX协调使用。我们也热切的期望看到两者能在Macromedia里一起工作。

十大理由第七名:采用率

AJAX 被业内领袖广泛采用证明了市场的欢迎程度和该技术组的正确。每一个该技术的使用者都成为了胜利者:包括google、yahoo、Amazon和微软等 等。是google地图吸引了WEB开发人员的目光,当人们开始调查是什么原因使得google有着如此惊人的用户体验的时候,人们揭开了罩在AJAX头 上的面纱。

当然,仅仅是google使用AJAX是不够使得这项技术跨越从支流到主流的鸿沟的。但是,如果你看一看使用AJAX技术的客户如eBusiness Applications (www.ebusinessapps.com) or Tibco (http://www.tibco.com/)等的表单时,你就会发现财富500强包括主要的金融机构、政府机构、航空公司和其他主要商业机构采用AJAX,并且在AJAX成为硬通货之前很早就开始使用了。

十大理由第八名:WEB2.0

喜 欢也好,厌恶也罢。WEB2.0运行吸引了开发人员、风险投资商、市场和最终用户等所有的目光。这些明确的促进了AJAX的早期应用。当大肆的宣传过去以 后,我们将会看到什么呢?从BackPack到google地图,AJAX界面是WEB2.0应用的主要的组成。大量的宣传有助于加速采用AJAX,而在 可用性上的获益会使得该技术被广发应用。WEB2.0的一个主要原则是使用WEB作为一个应用开发的平台,而不仅仅是一个网页。高的可用性和交互能力的用 户界面是一切应用平台的主要组成部分。

十大理由第九名:AJAX基于服务器技术的不确定性

和AJAX技术 的浏览器的独立性相同,该技术也兼容所有的标准型的服务器和服务端语言,如 PHP, ASP. ASP.Net, Perl, JSP, Cold Fusion等等,选择属于你的那种然后开始。这使得AJAX开发独立,因为所 有的开发人员都能使用并且一起讨论相同的表现层。

十大理由第十名:基于WEB的下一代RIA技术还没有出现

今 天就使用XUL技术开发应用的人是伟大的,因为现在90%的浏览器还不支持这种技术,对于大多数的实际应用来说,使用这种技术不切合实际。然而,AJAX 开发人员应该给出一部分的注意力在这些技术,如XAML 和XUL上。毫无疑问,这些技术将使开发富WEB应用变得简单。但是它们可能相互不兼容并且拥有 不同的市场需求或动力。

在今后一段时间,AJAX技术将极大的提高WEB应用的可用性。AJAX技术并不完美,不是“火箭科学”许多的开 发人员和技术公司始终在尝试RIA的其他更好的技术。而实际的问题是AJAX技术现在已经存在并且应用的很好,它跨浏览器、跨平台,而且不管是用户还是开 发人员都喜欢它的作用。特征鲜明的AJAX应用如google地图已经成为了本领域的领导者(还有人使用MapQuest吗?)同样的,领先的财富500 强使用AJAX技术并且贡献了开发工具给社区了。一般来说,业内在使用AJAX技术上取得了一致并且正在使用它。再强调一次,RIA应用和WEB应用使用 了AJAX获得的一个主要的优势不仅仅是开发人员的一个工具,而是一个现象:它改变了我们开发WEB应用的方式。没人能说得清楚在RIA应用方面,哪一种 技术会取代它,会在什么时候取代它;但是很多因素都支持AJAX应用应该持续好多年。

关于作者

Andre Charland 从事Internet软件开始超过十年之久,他是eBusiness Applications (www.ebusinessapps.com)公司的 主席和创建者之一。他和Dave Johnson在1998年创建了该公司。他主要的经验在可用性、市场、项目管理和基于构件的软件开发。所受教育包括: 在Vancouver, BC的Simon Fraser University,他在那里读计算机科学和工商管理。他作为开发者、管理者和架构师等不同 身份有上百个Internet项目的经验。

原文链接 http://www.developer.com/java/other/article.php/3567706

标签:

Joomla站点的迁移

以下以joomla站点迁移到IP为218.16.121.121的虚拟主机,域名为www.joomla.com
数据库帐号是:wcf,数据库密码是:88623132,数据库名是wcf,
请通过localhost连接。

1、 到你注册的域名的站点的域名管理中将www.joomla.com和joomla.com以“主机名/A记录”的形式解析到218.16.121.121。
2、 将数据库导出成sql脚本文件
2、将导出的sql脚本文件导入到迁移环境的数据库中。
3、修改configuration.php文件如下的配置
1) 修改$mosConfig_absolute_path 为你迁移后的站点的绝对路径
$mosConfig_absolute_path =’/www/winetcn/joomla;
2) 修伽$mosConfig_cachepath为你迁移后的站点的cache的路径
$mosConfig_cachepath = '/www/winetcn/joomla/cache';
3) 修改$mosConfig_db 为你迁移后的数据库名
$mosConfig_db = wcf;
4) 修改$mosConfig_live_site = 为你站点访问的URL。
$mosConfig_live_site = 'http://www.joomla.com';
5) 修改$mosConfig_password为你迁移后的数据的连接密码
$mosConfig_password = ‘88623132’;
6) 修改$mosConfig_user 为你迁移后的数据的访问用户名
$mosConfig_user = wcf;
4、如果整合了smf,需要修改smf的安装目录下的Settings.php文件中如下的配置:
1) 修改$boardurl为你迁移后的站点smf访问URL,如:$boardurl =’ http://www.wcfonline.com/forum’;
2) 修伽$db_name=’ wcf;
3) 修改$db_user = ‘wcf;
4) 修改$db_passwd = ‘88623132’;
5) 修改$boarddir= ' http://www.jooomla.com/forum;
6) 修改$sourcedir = '/www/winetcn/joomla/forum/Sources';
7) 登录joomla控制台,修改SMF桥接器的SMF中的绝对路径.
将其绝对路径修改为:/www/winetcn/joomla/forum
8) 以smf管理员的身份进入smf的管理控制台,修改smf风格的路径,管理——>目前所使用的风格
修改风格网址和图片地址分别为:
http://www.joomla.com/ forum/Themes/Neutron
http://www.joomla.com/ forum/ Themes/Neutron/images
所在文件夹修改为/www/winetcn/joomla/forum/ Themes/ Neutron

http://www.wcfonline.cn/content/view/12/27/

标签:

学习linux/unix编程方法的建议

建议学习路径

  首先先学学编辑器,vim, emacs什么的都行。
然后学make file文件,只要知道一点就行,这样就可以准备编程序了。

  然后看看《C程序设计语言》K&R,这样呢,基本上就可以进行一般的编程了,顺便找本数据结构的书来看。

  如果想学习UNIX/LINUX的编程,《APUE》绝对经典的教材,加深一下功底,学习《UNP》的第二卷。这样基本上系统方面的就可以掌握了。

  然后再看Douglus E. Comer的《用TCP/IP进行网际互连》第一卷,学习一下网络的知识,再看《UNP》的第一卷,不仅学习网络编程,而且对系统编程的一些常用的技巧就 很熟悉了,如果继续网络编程,建议看《TCP/IP进行网际互连》的第三卷,里面有很多关于应用协议telnet、ftp等协议的编程。
如果想写设备驱动程序,首先您的系统编程的接口比如文件、IPC等必须要熟知了,再学习《LDD》2。

  对于几本经典教材的评价:

  《The C Programing Language》K&R 经典的C语言程序设计教材,作者是C语言的发明者,教材内容深入浅出。虽然有点老,但是必备的一本手册,现在有时候我还常翻翻。篇幅比较小,但是每看一 遍,就有一遍的收获。另外也可用谭浩强的《C语言程序设计》代替。

  《Advanced Programing in Unix Envirement》 W.Richard Stevens:也是非常经典的书(废话,Stevens的书哪有不经典的!),虽然初学者就可以看,但是事实上它是《Unix Network Programing》的一本辅助资料。国内的翻译的《UNIX环境高级编程》的水平不怎么样,现在有影印版,直接读英文比读中文来得容易。

  《Unix Network Programing》W.Richard Stevens:第一卷讲BSD Socket网络编程接口和另外一种网络编程接口的,不过现在一般都用BSD Socket,所以这本书只要看大约一半多就可以了。第二卷没有设计到网络的东西,主要讲进程间通讯和Posix线程。所以看了《APUE》以后,就可以 看它了,基本上系统的东西就由《APUE》和《UNP》vol2概括了。看过《UNP》以后,您就会知道系统编程的绝大部分编程技巧,即使卷一是讲网络编 程的。国内是清华翻译得《Unix网络编程》,翻译者得功底也比较高,翻译地比较好。所以建议还是看中文版。

  《TCP/IP祥解》一共三卷,卷一讲协议,卷二讲实现,卷三讲编程应用。我没有怎么看过。,但是据说也很经典的,因为我没有时间看卷二,所以不便评价。

  《用TCP/IP进行网际互连》Douglus.E.Comer 一共三卷,卷一讲原理,卷二讲实现,卷三讲高级协议。感觉上这一套要比Stevens的那一套要好,就连Stevens也不得不承认它的第一卷非常经典。 事实上,第一卷即使你没有一点网络的知识,看完以后也会对网络的来龙去脉了如指掌。第一卷中还有很多习题也设计得经典和实用,因为作者本身就是一位教师, 并且卷一是国外研究生的教材。习题并没有答案,留给读者思考,因为问题得答案可以让你成为一个中级的Hacker,这些问题的答案可以象Douglus索 取,不过只有他只给教师卷二我没有怎么看,卷三可以作为参考手册,其中地例子也很经典。如果您看过Qterm的源代码,就会知道Qterm的telnet 实现部分大多数就是从这本书的源代码过来的。对于网络原理的书,我推荐它,而不是Stevens的《TCP/IP祥解》。

  《Operating System - Design and Implement》这个是讲操作系统的书,用Minix做的例子。作者母语不是英文,所以英文看起来比较晦涩。国内翻译的是《操作系统 设计与实现》,我没看过中文版,因为翻译者是尤晋元,他翻译的《APUE》已经让我失望头顶了。读了这本书,对操作系统的底层怎么工作的就会
有一个清晰的认识。

  《Linux Device Driver》2e ,为数不多的关于Linux设备驱动程序的好书。不过内容有些杂乱,如果您没有一些写驱动的经验,初次看会有些摸不着南北。国内翻译的是《Linux设备 驱动程序》第二版,第一版,第二版的译者我都有很深的接触,不过总体上来说,虽然第二版翻译的有些不尽人意,但是相比第一版来说已经超出了一大截。要读这 一本书,至少应该先找一些《计算机原理》《计算机体系结构》的书来马马虎虎读读,至少应该对硬件和计算机的工作过程有一些了解。

http://bbs.chinaunix.net/thread-1070966-1-1.html

标签:

星期日, 三月 30, 2008

Joomla学习,模块如何创建-3

好了。xml文件创建完毕,我们该进行下一步了,创建.php文件。
我们来看一下mod_latestnews.php。
/**
* @version $Id: mod_latestnews.php 9764 2007-12-30 07:48:11Z ircmaxell $
* @package Joomla
* @copyright Copyright (C) 2005 - 2008 Open Source Matters. All rights reserved.
* @license GNU/GPL, see LICENSE.php
* Joomla! is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
*/

// no direct access
defined('_JEXEC') or die('Restricted access'); // 不允许直接调用

// Include the syndicate functions only once
require_once (dirname(__FILE__).DS.'helper.php'); // 此处引用模型文件

$list = modLatestNewsHelper::getList($params); // 调用模型文件内的类,获得参数列表
require(JModuleHelper::getLayoutPath('mod_latestnews')); // 调用显示模块

标签: ,

Joomla学习,模块如何创建-2

按照我前面所做的分析,我在joomla的modules目录中创建了一个子目录,mod_demos,然后创建了一个基本没有什么内容的mod_demo.xml文件。



Demo module
Ou Lanhui
Mar. 30, 2008
All rights reserved.
Free for personal use but need denote if commercial use
ouland@gmail.com
http://www.ouland.com/
1.5.0
Demo module for joomla

mod_demo.php






在Joomla的界面里如果选择Extension->Module manager创建一个新的模块,就可以看到这个模块了,并且能够创建。至于它能否显示,因为mod_demo.php文件不存在,所以就不要想了。

标签: ,

Joomla学习,模块如何创建

模块的创建应该分成2个部分。感觉上,Joomla在实现的方面模块主要是用来处理View方面的工作。因为没有什么时间详细研究,好用即可就是标准。至于component的功能是什么现在没有什么感觉,Component能够生成页面,那应该是比module更为全面的吧,module明显具有只是页面某一部分的特点,因为在使用的时候你可以指定该模块显示在页面的某一位置,由index.php指定的位置。

今天青歌手有道题问的是赵州桥,看到图片问中国最古老的石拱桥是什么我也能答上来,但是是李春造的,在河北,并且是隋朝造的还是让人感触万千。希望我们的作品也能如此吧。我们从什么时候开始不注意质量的?我的思路有点问题,一讲下去就跑远了。不过对于作品,特别是软件,还是要了解好我们需要做什么,确保每一步不出现问题。虽然BUG可能还是会有,但是别出大问题吧。;)

说远了,回到模块上,大的上面模块应该有2个部分,即用于界面显示的,放置在modules里的,还有放在administrator/modules里,用来配置模块的管理部分。哦,现在问题有点清楚了,components,应该是用来具体实现功能的吧。(乱讲)比如想做的一个具体信息分类管理,后台采用模块进行分类的管理,前台呢,按我们计划的有分类的列表视图,分类下的信息展示视图,都可以做为页面的内容。再看后台的管理介面,模块中有mod_mainmenu模块用于显示主栏目菜单,而主栏目菜单上的项目由component对应的视图指定,又倒过来了吧,modules->components。反正是分析着玩,等分析完再对照能找得到的文档看看,这活干得也有意思一点。

好,说完这两种模块,即普通模块和管理模块。我们看看做一个普通模块要做什么事情。首先要在/modules下建立一个目录,这个目录就是你想创建的模块了,通常情况下,它具有这样的名字mod_<模块名>。比如我们要做一个用户想要实现的界面功能的收集系统(这个就是我们以后的参照系统吧),目标是这样:每个用户都可以注册一个或者多个项目,每个项目从属于某一个或多个分类,感谢google,我从你们的邮件系统里学到了label方式,就是每个用户可以指定多个标记来说明项目所属的分类。每个项目用户都需要输出项目相关的内容,我们来想一下,项目的描述,项目的规模,当然,用户可以选择这个项目能不能被别人看到,而能被别人看到的内容,网站的运营者应该审核之后才能被看到,这样防止用户把自己的联系方式都直接写上,这样网站就没有办法赢利了。;)然后用户可以将他所策划的功能做以描述,就是填写另外一张表,来说明系统有什么功能,每个功能是怎么回事。难道我要做的是一个需求管理工具?;)好,其实就是这么一回事了。想太多这个模块没法子写了。

创建好目录之后,首先我们需要几个文件,用于描述模块的文件mod_<模块名>.xml,模块的主文件mod_<模块名>.php也应该是模块的控制文件吧,模块的模型文件helper.php,然后是模块的视图目录tmpl,该目录下有default.php作为缺省的视图。Joomla对MVC的设计非常好,可以的是我知道MVC,但是知其然而不知其所以然,因为我一直是实用主义者,可用是首先,就象我虽然了解设计模式,但是并不强求设计模式的严格使用。我向ACE,Boost的作者致敬,但不表示我不崇尚更为敏捷的开发过程。

描述模块的文件我们看看:

-- 安装信息,指明模块的版本
Latest News -- 模块的名称
Joomla! Project -- 作者
July 2004 -- 创建日期
Copyright (C) 2005 - 2008 Open Source Matters. All rights reserved.
-- 版权信息
http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
-- 采用的协议类型,GPL的后续还可能是什么?
admin@joomla.org
-- 开发者的email地址
www.joomla.org-- 作者的网站地址
1.5.0 -- 这个版本是什么版本?适用于Joomla的版本?
DESCLATESTNEWS
-- 模块的描述信息,相当于商业软件的鼓吹信息
-- 文件列表
mod_latestnews.php
-- 文件的名称

-- 模块的参数

-- 单个参数的内容指定
-- name-参数名 type-参数类型 default-缺省值 label-标签显示内容 description - 参数的相关描述


























标签: ,

星期五, 三月 28, 2008

WIN32下DELPHI中的多线程【变量存储】(三)

线程中的变量
由于每个线程都代表了一个不同的执行路径,因此,最好有一种只限于一个线程内部使用的数据,
要实现上述目的有以下几种方式:
1、局部变量(基于栈),很简单,在你的线程函数中你定义的变量既是如此。由于每个线程都在各自的栈中,各个线程将都有一套局部变量的副本,这样,就不会相互影响。对于那些只在过程或函数的生存期有意义的变量,应当把它们声明为局部变量。
2、存储在线程对象中。还记得createthread函数中的lpparameter参数吗,它可以接受一个无类型的指针。结合本文第一章的内容,你应 该还记得,它被存储在线程内核对象的上下文结构中,你可以通过context结构中的context_integer部分的ebx来读取它的地址。
下面是一段示例代码,用来演示读取context结构,这段代码一般用不到,但它可以说明cratethread函数中的lpparameter被存储的位置

{
作者:wudi_1982
联系方式:wudi_1982@hotmail.com
转载请著名出处,本代码为演示代码,只贴出了一些关键部分
}


type
//传递给线程函数的结构和指针的声明
tinfo = record
count : integer;
x : integer;
y : integer;
end;
pinfo
= ^tinfo;

var
mythreadhad : thandle;
//一个全局变量,用来保存线程的句柄

//线程函数
function mythread(info : pointer):dword; stdcall;
var
i : integer;
begin
//根据传递来信息决定在窗口的那个位置输出什么信息
for i := 0 to pinfo(info)^.count-1 do
form1.image1.canvas.textout(pinfo(info)
^.x,pinfo(info)^.y,inttostr(i));
//freemem(info);
result := 0;
end;

//创建一个线程
procedure tform1.button4click(sender: tobject);
var
ppi : pinfo;
mythreadid : dword;
begin
//分配空间并赋初值
ppi :=allocmem(sizeof(tinfo));
ppi
^.count := 1000000;
ppi
^.x := 10;
ppi
^.y := 10;
//创建
mythreadhad := createthread(nil,0,@mythread,ppi,create_suspended,mythreadid);
//在窗体上显示线程函数的地址和传递给它的参数的地址
labthreadaddr.caption := inttostr( integer(@mythread));
labthreadpvparam.caption :
= inttostr(integer(ppi));
end;

//读取context结构,注意context结构是和cpu有关的,我这里测试时,工作在intel的cpu上
procedure tform1.btnrcontextclick(sender: tobject);
var
con : _context;
begin
//初始化结构
con.contextflags := context_full;
//读取
getthreadcontext(mythreadhad,con);
//显示在窗体的listbox上
with lbxcontextinfo.items do
begin
// clear;
add(------------context--------------);
add(
);
add(
context_debug_registers-----);
add(
dr0:+#9+inttostr(con.dr0));
add(
dr1:+#9+inttostr(con.dr1));
add(
dr2:+#9+inttostr(con.dr2));
add(
dr3:+#9+inttostr(con.dr3));
add(
dr6:+#9+inttostr(con.dr6));
add(
dr7:+#9+inttostr(con.dr7));
add(
context_segments---------);
add(
seggs:+#9+inttostr(con.seggs));
add(
segfs:+#9+inttostr(con.segfs));
add(
seges:+#9+inttostr(con.seges));
add(
segds:+#9+inttostr(con.segds));
add(
context_integer.---------);
add(
edi: +#9+inttostr(con.edi));
add(
esi: +#9+inttostr(con.esi));
add(
ebx: +#9+inttostr(con.ebx));
add(
edx: +#9+inttostr(con.edx));
add(
ecx: +#9+inttostr(con.ecx));
add(
eax: +#9+inttostr(con.eax));
add(
context_control----------);
add(
ebp: +#9+inttostr(con.ebp));
add(
eip: +#9+inttostr(con.eip));
add(
segcs: +#9+inttostr(con.segcs));
add(
eflags: +#9+inttostr(con.eflags));
add(
esp: +#9+inttostr(con.esp));
add(
segss: +#9+inttostr(con.segss));
end;

end;

把上面代码整理之后,添加到你的程序中,你可以发现(如果也是intel的cpu),那么你可以从eax寄存器读取到线程函数的地址,从ebx中读取到传递给线程函数的参数地址。
在delphi中的tthread对象的构造函数中,你可以看到这段代码
fhandle := beginthread(nil, 0, @threadproc, pointer(self), create_suspended, fthreadid);
再 观察beginthread的实现,你会发现tthread的调用createthread时,将pointer(self),也就是tthread对象 本身当作线程函数的参数传递过去,换言之,你在tthread的派生类中定义的变量,对于一个线程而言,将存储在这个线程单独的堆栈中,而它在堆栈的地址 存储在线程的上下文结构中。
可以做一个简单的试验,将一个线程生成多次,你可以发现存储在线程对象内部的变量将互不影响。
说到这里,必须谈论一个问题,效率的问题,我在一本书上曾经看到过这样一段话“由于访问线程对象中的数据比访问线程局部变量要快10倍,因此,你应当尽可 能地把线程专用的信息保存在线程对象中。”对此,我一直没有特别理解。如果一定要相信这句话,那我会这么理解,就是存储在线程对象中的变量因为上下文结构 记录了它的地址等原因,所以它更快。尽信书不如无书,我还在思考,不过好在这种速度的影响对于通常的使用而言影响不大。

3、在delphi中,用object pascal的关键字threadvar来声明变量,以利用操作系统级的线程局部存储。
在前面我们了解到:虽然对于局部变量,在每个线程中都一个副本,然而应用程序的全局变量是被所有线程所共享的。当多个线程对这个全局变量进行访问时,将可 能出现很多未知的问题,win32提供了一种称为线程局部存储的方式,它能使你在第一个运行的线程中创建一个全局变量的拷贝。delphi利用关键字 threadvar封装此功能。在threadvar关键字下你可以声明任何局部存储的变量。
4、全局变量,多线程最让人头疼的地方就是全局变量 了,好的同步方式将决定你高效、安全的访问全局变量,虽然上述的threadvar是解决全局变量线程局部存储的一个办法,但在我实际的编码工作中,几乎 很少用它,它的局限性太多。多线程访问全局变量的方法将在下一文中详细描述。

http://www.west263.com/www/info/41142-1.htm

标签: ,

WIN32下DELPHI中的多线程【线程的调度】(二)

线程的调度

每个线程是拥有一个上下文结构的,这个结构维护在线程的内核对象中。这个上下文结构反映了线程上次运行时该线程的c p u寄存器的状态。每隔20ms左右,windows要查看当前存在的所有线程内核对象。在这些对象中,只有某些对象被视为可以调度的对象。windows 选择可调度的线程内核对象中的一个,将它加载到c p u的寄存器中,它的值是上次保存在线程的环境中的值。这项操作称为上下文转换。windows实际上保存了一个记录,它说明每个线程获得了多少个运行机 会。
windows被称为抢占式多线程操作系统,因为一个线程可以随时停止运行,随后另一个线程可进行调度。如你所见,可以对它进行一定程度的控制,但是不能 太多。注意,无法保证线程总是能够运行,也不能保证线程能够得到整个进程,无法保证其他线程不被允许运行等等。
我在编写串口通讯程序的时候,起初,我有一个天真的想法,“在win32平台下,如何能够保证从串口传送过来的数据,在数据到达后1ms内开始运行?”。 为此,我曾经做了许多试验,但当我真正了解了一些win32平台的知识,我得到了答案,办不到。只有实时操作系统才能作出这样的承诺,但windows不 是实时操作系统。实时操作系统必须清楚地知道它是在什么硬件上运行,这样它才能知道它的硬盘控制器和键盘等的等待时间。microsoft对 windows规定的目标是,使它能够在各种不同的硬件上运行,即能够在不同的cpu、不同的驱动器和不同的网络上运行。简而言之,windows没有设 计成为一种实时操作系统。
windows系统只调度可以调度的线程。那么什么是可以调度的线程,什么是不可以调度的线程呢?例如,有些线程对象的暂停计数大于1(记录在线程内核对 象的上下文结构中)。这意味着该线程已经暂停运行,不应该给它安排任何c p u时间。还记得上文中曾经提到的create_suspended标志吗?在创建一个线程的时候,createthread函数接收的倒数第二个参数中赋 值create_suspended就可以创建一个暂停的线程。除了暂停的线程外,其他许多线程也是不可调度的线程,因为它们正在等待某些事情的发生。例 如,如果记事本程序,如果你不键入任何数据,那么它的线程就没有什么事情要做。系统不给无事可做的线程分配cpu时间。当移动它的窗口时,或者它的窗口需 要刷新它的内容,或者将数据键入记事本,系统就会自动使它的线程成为可调度的线程。但切记,这并不意味着它的线程立即获得了cpu时间。它只是表示记事本 的的线程有事情可做,系统将设法在某个时间(不久的将来)对它进行调度。

线程的暂停和执行
我们前面说过,在线程内核对象的内部有一个值,用于指明线程的暂停计数。当调用createthread函数时,就创建了线程的内核对象,并且它的暂停计 数被初始化为1。这可以防止线程被调度到cpu中。当然,这是很有用的,因为线程的初始化需要时间,你不希望在系统做好充分的准备之前就开始执行线程。当 线程完全初始化好了之后, 要查看是否已经传递了create_suspended标志。如果已经传递了这个标志,那么这些函数就返回,同时新线程处于暂停状态。如果尚未传递该标 志,那么该函数将线程的暂停计数递减为0。当线程的暂停计数是0的时候,除非线程正在等待其他某种事情的发生,否则该线程就处于可调度状态。
在暂停状态中创建一个线程,就能够在线程有机会执行任何代码之前改变线程的运行环境(如优先级)。一旦改变了线程的环境,必须使线程成为可调度线程。要进 行这项操作,可以调用resumethread,将线程句柄传递给它,如果resumethread,函数运行成功,它将返回线程的前一个暂停计数,否则 返回0xffffffff。注意这里,它返回的是前一个暂停计数。
单个线程可以暂停若干次。如果一个线程暂停了3次,它必须恢复3次,然后它才可以被分配给一个c p u。当创建线程时,除了使用create_suspended外,也可以调用suspendthread函数来暂停线程的运行。任何线程都可以调用该函数 来暂停另一个线程的运行(只要拥有线程的句柄)。不用说,线程可以自行暂停运行,但是不能自行恢复运行。suspendthread返回的是线程的前一个 暂停计数。线程暂停的最多次数可以是maximum_suspend_count次。值得注意的是,suspendthread与内核方式的执行是异步进 行的,但是在线程恢复运行之前,不会发生用户方式的执行。在实际环境中,调用suspendthread时必须小心,因为不知道暂停线程运行时它在进行什 么操作。如果线程试图从堆栈中分配内存,那么该线程将在该堆栈上设置一个锁。当其他线程试图访问该堆栈时,这些线程的访问就被停止,直到第一个线程恢复运 行。只有确切知道目标线程是什么(或者目标线程正在做什么),并且采取强有力的措施来避免因暂停线程的运行而带来的问题或死锁状 态,suspendthread才是安全的。

线程的睡眠
线程也能告诉系统,它不想在某个时间段内被调度。这是通过调用sleep函数来实现的:
void sleep(dword cmilliseconds)

该函数可使线程暂停自己的运行,直到cmilliseconds过去为止。关于sleep函数,有下面几个重要问题值得注意:
• 调用sleep,可使线程自愿放弃它剩余的时间片。
• 系统将在大约的指定毫秒数内使线程不可调度。不错,如果告诉系统,想睡眠100ms,那么可以睡眠大约这么长时间,但是也可能睡眠数秒钟或者数分钟。还是 那个反复重申的概念, windows不是个实时操作系统。虽然线程可能在规定的时间被唤醒,但是它能否做到,取决于系统
中还有什么操作正在进行。
• 可以调用sleep,并且为cmilliseconds)参数传递infinite。这将告诉系统永远不要调度该线程。这不是一件值得去做的事情。最好是让线程退出,并还原它的堆栈和内核对象。
• 可以将0传递给sleep。这将告诉系统,调用线程将释放剩余的时间片,并迫使系统调度另一个线程。但是,系统可以对刚刚调用sleep的线程重新调度。 如果不存在多个拥有相同优先级的可调度线程,就会出现这种情况。sleep(0)是一个非常有意思的方法。要小心sleep()神秘的时间调整问题。 sleep()可能会使你的机器出现特别的问题。这种问题在另一台机器上可能无法再现。

切换到另一个线程
系统提供了一个称为switchtothread的函数,使得另一个可调度线程(如果存在能够运行)。当调用这个函数的时候,系统要查看是否存在一个迫切 需要c p u时间的线程。如果没有线程迫切需要c p u时间switchtothread就会立即返回。如果存在一个迫切需要c p u时间的线程,switchtothread就对该线程进行调度(该线程的优先级可能低于调用switchtothread的线程)。这个迫切需要c p u时间的线程可以运行一个时间段,然后系统调度程序照常运行。该函数允许一个需要资源的线程强制另一个优先级较低、而目前却拥有该资源的线程放弃该资源。 如果调用switchtothread函数时没有其他线程能够运行,那么该函数返回false,否则返回一个非0值。调用switchtothread函 数与调用sleep是相似的,差别是switchtothread允许优先级较低的线程运行。即使低优先级线程迫切需要cpu时间,而sleep则可能因 为优先级关系使得刚放弃cpu的线程被立即重新调度。

优先级
操作系统会负责为每个线程分配cpu时间。一个线程所分配到的cpu时间主要取决于该线程的优先级,而线程的优先级又取决于进程的优先级类和线程本身的相对优先级。
1. 进程的优先级类
进程的优先级类用来描述一个进程的优先程度。win32支持四种不同的优先级类: idle、normal、high 和realtime。其中,normal是默认的优先级。在windows单元中,每一种优先级类都对应着一个标志。当要进行进程的优先级设置时,可以用 一种优先级类与createprocess()的参数dwcreationflags进行或操作。另外,还可以动态地为一个已有的进程调整优先级类。这时 候,通常你要用到下面api函数
bool setpriorityclass(handle hprocess,dword fdwpriority),其中第一个参数是进程的句柄,你可以通过getcurrentprocess来获得当前进程的句柄。每个优先级类也对应一个数 字,值在4~ 24之间。注意在windows nt/2000下,要有特殊的权限才能修改进程的优先类。默认的设置允许进程设置它们的优先级类,但是,这些都可以由系统管理员来关闭,尤其是在高负载的 winnt/2000服务器上。
大多数情况下,进程的优先级类不要被设为realtime。因为,大多数操作系统本身的线程的优先级类比realtime低。如果一个进程得到的c p u时间比操作系统本身还多,后果是无法想象的。即使将进程的优先级类设为high ,也可能引起问题。因为,当高优先级的线程没有大部分空时间或等待外部事件时,它要从低优先级的线程和进程中抢夺cpu时间,直到它被一事件阻塞或处于空 闲状态或处理消息。所以,在抢占式多任务操作系统中如果不能合理地安排优先级,就很容易崩溃。

优先级类 说明
实时 进程中的线程必须立即对事件作出响应,以便执行关键时间的任务。
该进程中的线程还会抢先于操作系统组件之前运行。使用本优先级类
时必须极端小心
进程中的线程必须立即对事件作出响应,以便执行关键时间的任务。
task manager(任务管理器)在这个类上运行,以便用户可以撤消脱
离控制的进程
高于正常 进程中的线程在正常优先级与高优先级之间运行(这是wi n d o w s
2 0 0 0中的新优先级类)
正常 进程中的线程没有特殊的调度需求
低于正常 进程中的线程在正常优先级与空闲优先级之间运行(这是wi n d o w s
2 0 0 0中的新优先级类)
空闲 进程中的线程在系统空闲时运行。该进程通常由屏幕保护程序或后
台实用程序和搜集统计数据的软件使用

2. 相对优先级
决定一个线程全面的优先级的另一方面是相对优先级。优先级类是针对进程的,它对进程内部的
所有线程都有效。而相对优先级是针对某个线程的。一个线程的相对优先级可设为以下七种: idle、lowest、below normal、normal、above normal、highest 和time critical。
要 设置一个线程的相对优先级,可以通过api函数setthreadpriority来完成,再delphi中,你可以通过tthread对象的 priority属性来设置。获得线程相对优先级的api函数是int getthreadpriority(handle hthread);

系统何如根据优先级来调度线程
每个线程都会被赋予一个从0(最低)到31(最高)的优先级号码。当系统确定将哪个线程分配给cpu时,它首先观察优先级为31的线程,并以循环方式对它 们进行调度。如果优先级为31的线程可以调度,那么就将该线程赋予一个cpu。在该线程的时间片结束时,系统要查看是否还有另一个优先级为31的线程可以 运行,如果有,它将允许该线程被赋予一个cpu。只要优先级为31的线程是可调度的,系统就绝对不会将优先级为0到30的线程分配给c p u。这种情况称为渴求调度(starvation)。当高优先级线程使用大量的cpu时间,从而使得低优先级线程无法运行时,便会出现渴求情况。在多处理 器计算机上出现渴求情况的可能性要少得多,因为在这样的计算机上,优先级为31和优先级为30的线程能够同时运行。系统总是设法使cpu保持繁忙状态,只 有当没有线程可以调度的时候, cpu才处于空闲状态。
人们可能认为,在这样的系统中,低优先级线程永远得不到机会运行。不过正像前面指出的那样,在任何一个时段内,系统中的大多数线程是不能调度的。例如,如 果进程的主线程调用getmessage函数,而系统发现没有线程可以供它使用,那么系统就暂停进程的线程运行,释放该线程的剩余时间片,并且立即将 cpu分配给另一个等待运行的线程。如果没有为getmessage函数显示可供检索的消息,那么进程的线程将保持暂停状态,并且决不会被分配给cpu。 但是,当消息被置于线程的队列中时,系统就知道该线程不应该再处于暂停状态。此时,如果没有更高优先级的线程需要运行,系统就将该线程分配给一个cpu。

高优先级线程将抢在低优先级线程之前运行,不管低优先级线程正在运行什么。例如,如果一个优先级为5的线程正在运行,系统发现一个高优先级的线程准备要运 行,那么系统就会立即暂停低优先级线程的运行(即使它处于它的时间片中),并且将c p u分配给高优先级线程,使它获得一个完整的时间片。还有,当系统引导时,它会创建一个特殊的线程,称为0页线程。该线程被赋予优先级0,它是整个系统中唯 一的一个在优先级0上运行的线程。当系统中没有任何线程需要执行操作时,0页线程负责将系统中的所有空闲r a m页面置0。

动态提高线程的优先级等级
通过将线程的相对优先级与线程的进程优先级类综合起来考虑,系统就可以确定线程的优先级等级。有时这称为线程的基本优先级等级。

系统常常要提高线程的优先级等级,以便对窗口消息或读取磁盘等i/o事件作出响应。
例如,在高优先级类进程中的一个正常优先级等级的线程的基本优先级等级是13。如果用户按下一个操作键,系统就会将一个wm_keydown消息放入线 程的队列中。由于一个消息已经出现在线程的队列中,因此该线程就是可调度的线程。此外,键盘设备驱动程序也能够告诉系统暂时提高线程的优先级等级。该线程 的优先级等级可能提高2级,其当前优先级等级改为15。系统在优先级为15时为一个时间片对该线程进行调度。一旦该时间片结束,系统便将线程的优先级递减 1,使下一个时间片的线程优先级降为14。该线程的第三个时间片按优先级等级13来执行。如果线程要求执行更多的时间片,均按它的基本优先级等级13来执 行。注意,线程的当前优先级等级决不会低于线程的基本优先级等级。此外,导致线程成为可调度线程的设备驱动程序可以决定优先级等级提高的数量。 microsoft并没有规定各个设备驱动程序可以给线程的优先级提高多少个等级。这样就使得microsoft可以不断地调整线程优先级提高的动态等 级,以确定最佳的总体响应性能。系统只能为基本优先级等级在1至15之间的线程提高其优先级等级。实际上这是因为这个范围称为动态优先级范围。此外,系统 决不会将线程的优先级等级提高到实时范围(高于15)。由于实时范围中的线程能够执行大多数操作系统的函数,因此给等级的提高规定一个范围,就可以防止应 用程序干扰操作系统的运行。另外,系统决不会动态提高实时范围内的线程优先级等级。
另一种情况也会导致系统动态地提高线程的优先级等级。比如有一个优先级为4的线程准备运行但是却不能运行,因为一个优先级为8的线程正连续被调度。在这种 情况下,优先级为4的线程就非常渴望得到cpu时间。当系统发现一个线程在大约3至4s内一直渴望得到c p u时间,它就将这个渴望得到cpu时间的线程的优先级动态提高到15,并让该线程运行两倍于它的时间量。当到了两倍时间量的时候,该线程的优先级立即返回 到它的基本优先级。
系统动态的改变优先级,在我们编程的时候会产生不良影响,为此,还有两个api函数可以使得系统的此功能不起作用。
bool setprocesspriorityboost(handle hprocess,bool disablepriorityboost);
bool setthreadpriorityboost(handle hthread,bool disablepriorityboost);
从名字你就应该可以看出,第一个api函数可以激活或停用指定进程所有线程的优先级提高功能,而后面一个则是针对特定线程的。

例子:关键的代码如下

{
作者:wudi_1982
联系方式:wudi_1982@hotmail.com
转载请著名出处
本代码旨在演示线程的调度,很多位置没有加入适当的控制和资源释放,请按照后续操作执行
}

type
tsleeptype
=(stsleep,stswitch);

//演示线程调度的tthread派生类
tprithread1=class(tthread)
private
curcount : integer;
//当前计数
flb : tlabel; //用来显示当前计数的label
fcansleep : boolean; //是否自动释放时间片
fsleepms : integer;
fsleeptype : tsleeptype;
//释放时间片的方式
procedure getrestult;
protected
procedure execute;
override;
public
constructor create(createsuspended: boolean;alabel : tlabel);
property cansleep : boolean read fcansleep write fcansleep;
property sleepms : integer read fsleepms write fsleepms;
property sleeptype : tsleeptype read fsleeptype write fsleeptype;
end;

....

{ tprithread1的实现 }

constructor tprithread1.create(createsuspended: boolean; alabel: tlabel);
begin
//构造函数
flb := alabel;
fsleepms :
= 0;
fcansleep :
= true;
fsleeptype :
= stsleep;
inherited create(createsuspended);
end;

procedure tprithread1.execute;
var
i : integer;
begin
inherited;
freeonterminate :
= true;
curcount :
= 0;
for i := 0 to 100000 do
begin
curcount :
= i;//改变当前计数
synchronize(getrestult);//显示结果
if fcansleep then//是否自动释放时间片
begin
//根据释放时间片的不同方式进行相应操作
case fsleeptype of
stsleep : sleep(sleepms);
//睡眠
stswitch : switchtothread;//调用其他线程
end;
end;
end;
end;

procedure tprithread1.getrestult;
begin
flb.caption :
= inttostr(curcount);
end;

{form1的主要代码}
procedure tform1.btnpthread1createclick(sender: tobject);
begin
//生成两个线程
mypthread1 := tprithread1.create( not ckbx1state.checked,lab1);
mypthread2 :
= tprithread1.create(not ckbx2state.checked,lab2);
//得到他们当前的优先级
lb1p.caption := inttostr(getthreadpriority(mypthread1.handle));
lb2p.caption :
= inttostr(getthreadpriority(mypthread2.handle));

end;

procedure tform1.btnpthread1resclick(sender: tobject);
begin
//执行线程
mypthread1.resume;
ckbx1state.checked :
= true;

mypthread2.resume;
ckbx2state.checked :
= true;
end;

procedure tform1.btnpthread1sudclick(sender: tobject);
begin
//挂起线程
mypthread1.suspend;
ckbx1state.checked :
= false;
mypthread2.suspend;
ckbx2state.checked :
= false;
end;

procedure tform1.btnuppthread1click(sender: tobject);
begin
//在线程挂起时,提高第一个线程的相对优先级
mypthread1.priority := tphigher;
//显示当前的优先级到屏幕
lb1p.caption := inttostr(getthreadpriority(mypthread1.handle));
// mypthread2.priority := tphigher;
end;

procedure tform1.btnupdatesleepclick(sender: tobject);
begin
//修改两个线程的时间片释放方式
mypthread1.cansleep := ckbxallowsleep1.checked;
case radiogroup1.itemindex of
0 : mypthread1.sleeptype := stsleep;
1 : mypthread1.sleeptype := stswitch;
end;

mypthread2.cansleep :
= ckbxallowsleep2.checked;
case radiogroup2.itemindex of
0 : mypthread2.sleeptype := stsleep;
1 : mypthread2.sleeptype := stswitch;
end;

end;

窗体效果:

线程调度程序的界面

让我们来用这个程序测试一些效果:
1、基本执行。程序运行之后,使用默认设置,点击【创建线程】按钮,线程将被创建,并且挂起,这是你可以 间隔的点击【执行线程】和【挂起线程】按钮,你会在屏幕上看到线程的当前计数,注意这两个计数之间的差值,以及整个界面的执行效果(我指的是在你让线程不 断的执行和挂起之间界面是否会出现不刷新的情况),当线程执行完毕之后,关闭程序。
2、通过sleep(0)释放时间片演示线程调度。运行程序, 使用默认设置,点击【创建线程】按钮,然后将两个线程的自释放时间片功能统统去掉(也就是去掉ckbxallowsleep1 and 2的勾勾),然后点击【修改睡眠方式】按钮,随后你可以进行间隔点击【执行线程】和【挂起线程】按钮,多做几次这样的操作,观察两个计数之间的差值,和测 试1的差值比较一下。我想你应该能想到些什么。然后,几乎可以肯定你的界面将会出现无法刷新的情况,并且你的鼠标无法立即在此界面上进行其他的操作。这个 时候,稍等一下,你会发现过了一会儿,两个当前计数都被刷新了。为什么??这时,我们除了考虑我们创建的两个线程之外,你还要考虑的你程序本身的主线程以 及其他可能存在的附属线程,我们再去程序中线程的那段循环代码,
curcount := i;//改变当前计数
synchronize(getrestult);//显示结果
if fcansleep then//是否自动释放时间片
begin
//根据释放时间片的不同方式进行相应操作
case fsleeptype of
stsleep : sleep(sleepms);//睡眠
stswitch : switchtothread;//调用其他线程
end;
end;
你应该看到线程将当前计数显示在屏幕上的操作是执行了synchronize(getrestult),这里,因为我们的线程和vcl界面发生了交互,我 们必须对synchronize有所了解,去看vcl的源码,你会发现,当你在程序中第一次创建一个附属线程时, vcl将会从主线程环境中创建和维护一个隐含的线程窗口。此窗口唯一的目的是把通过synchronize()调用的方法排队。 synchronize()把由method参数传递过来的方法保存在tthread的fmethod字段中,然后,给线程窗口发一个 cm_execproc消息,并且把消息的lparam参数设为self(这里指线程对象)。当线程窗口的窗口过程收到这个消息后,它就调用 fmethod字段所指定的方法。由于线程窗口是在主线程内创建的,线程窗口的窗口过程也将被主线程执行。因此,fmethod字段所指定的方法就在主线 程内执行。
在我们选择释放时间片的模式下,在这里,无论我们是用sleep还是switchtothread,当前线程都会立即释放时间片,因为这时我们并没有修改 线程的优先级,他们都在同样的优先级环境下运行,那么当占用cpu的线程释放时间片后,其他线程将可以相对轻松的得到cpu,所以在使用释放时间片的模式 下,界面的刷新会良好。并且调度相对有序。
3、sleep和switchtothread区别的演示。运行程序,使用默认设置,点击【创建线程】 按钮,然后点击【提高线程1的优先级】按钮,再点击【执行线程】这是,两个线程将不再是同样的优先级,其他设置依然是默认的(使用sleep方式释放时间 片),你会看到线程1首先执行,线程2处于可调度模式,但并没有被调度(当前计数没有刷新),并且屏幕也不刷新,在稍等一段时间之后,屏幕刷新,线程2也 开始运行,并且此时屏幕刷新正常。为什么呢?回头去看本文上面的内容,当线程1的优先级提高之后,系统会首先调度它,虽然它使用sleep(0)来释放时 间片,但当时间片释放后,因为它的优先级相对较高,系统依然会调度线程1,所以此时,线程2将不能执行,界面也不能有效刷新。在这个思路下,再做一个测 试,使用同样的方式,只不过这次,在线程执行之前,除了提高线程1的优先级之外,还将线程1释放时间片方式改为switchtothread,此时你就可 以看到两个线程都有机会执行,并且界面也将有效刷新。
4、你还可以做其他配置信息的测试,相信会加深对win32平台下线程调度的了解。

参考文献
1、《delphi5开发人员指南》
2、《windows核心编程》


http://www.west263.com/www/info/41141-1.htm

标签: ,

WIN32下DELPHI中的多线程【深入VCL源码】

线程的基础知识
线程的组成。线程有两部分组成。
1、一个是线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。
2、另一个是线程堆栈,它用于维护线程在执行代码时需要的所有函数参数和局部变量。
进 程从来不执行任何东西,它只是线程的容器。线程总是在某个进程环境中创建的,而且它的整个寿命期都在该进程中。这意味着线程在它的进程地址空间中执行代 码,并且在进程的地址空间中对数据进行操作。因此,如果在单进程环境中,你有两个或多个线程正在运行,那么这两个线程将共享单个地址空间。这些线程能够执 行相同的代码,对相同的数据进行操作。这些线程还能共享内核对象句柄,因为句柄表依赖于每个进程而不是每个线程存在。
线程是一种操作系统对象,它表示在进程中代码的一条执行路径。在每一个wi n32的应用程序中都至少有一个线程,它通常被称为主线程或默认线程。在应用程序中也可以自由地创建别的线程去执行其他任务。线程技术使不同的代码可以同 时运行。当然,只有在多c p u的计算机上,多个线程才能够真正地同时运行。在单个cpu上,由于操作系统把c p u的时间分成很短的片段分配给每个线程,这样给人的感觉好像是多个线程真的同时运行,他们只是“看起