最近Ken在比较系统地学习Delphi多线程编程方面的知识,在网络上查阅了很多资料。现在Ken将对这些资料进行整理和修改,以便收藏和分享。内容基本上是复制粘贴,拼拼凑凑,再加上一些修改而来。各个素材的来源已经很难搞清楚,因此不再一一说明。一些资料可能有点老,但仍然有参考价值。篇幅比较长,耐心点看完吧。

多线程共存于应用程序中是现代操作系统中的基本特征和重要标志。为了提高程序的运行效率,在操作系统中提出了进程和线程的概念,在一个进程中可以包含多个线程,进程作为资源分配的基本单位,线程作为独立运行和独立调度的基本单位。关于多线程的更详细说明建议粗略地阅读下百度百科《多线程》。现在PC的硬件性能越来越好,多线程能够充分发挥这些性能,让程序的用户体验更好。

多线程的两个概念:

1)进程:也称任务,程序载入内存,并分配资源,称为“一个进程”。

注意:进程本身并不一定要正在执行。进程由以下几部分组成:

a>一个私有的地址空间,它是进程可以使用的一组虚拟内存地址空间;

b>程序的相关代码、数据源;

c>系统资源,比如操作系统同步对象等;

d>至少包含一个线程(主线程);

2)线程:是程序的执行单位(线程本身并不包括程序代码,真正拥有代码的是进程),每个进程至少包括一个线程,称为主线程,一个进程如果有多个线程,就可以共享同一进程的资源,并可以并发执行。

线程是进程的一个执行单元,是操作系统分配CPU 时间的基本实体,线程主要由如下两部分组成:

a>数据结构;

b>CPU 寄存器和堆栈;

一个进程中的线程,可以独立运行,也可以控制另一个线程的运行。

多线程带来如下好处:

1)避免瓶颈;

2)并行操作;

3)提高效率;

在多线程中,通过优先级管理,可以使重要的程序优先操作,提高了任务管理的灵活性。

另一方面,在多CPU 系统中,可以把不同的线程在不同的CPU 中执行,真正做到同时处理多任务(Win 98 只是模拟的,而Win/NT/2000是真正的多CPU同时操作)。

也有潜在的坏处:

1)写得不好更容易出现未知错误;

2)如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换;

3)更多的线程需要更多的内存空间;

4)线程的中止需要考虑其对程序运行的影响。

当然只要熟悉多线程编程,这些坏处都可以尽量避免的。

下面提供一些代码,供大家在尝试中去理解。

procedure TForm1.Button1Click(Sender: TObject); 
var 
  i: Integer; 
begin 
  for i := 0 to 500000 do 
  begin 
    Canvas.TextOut(10, 10, IntToStr(i)); 
  end; 
end;

上面程序运行时, 我们的窗体基本是 "死" 的, 可以在你在程序运行期间拖动窗体试试...

Delphi 为我们提供了一个简单的办法(Application.ProcessMessages)来解决这个问题:

procedure TForm1.Button1Click(Sender: TObject); 
var 
  i: Integer; 
begin 
  for i := 0 to 500000 do 
  begin 
    Canvas.TextOut(10, 10, IntToStr(i)); 
    Application.ProcessMessages; 
  end; 
end;

这个 Application.ProcessMessages; 一般用在比较费时的循环中, 它会检查并先处理消息队列中的其他消息.

但这算不上多线程, 譬如: 运行中你拖动窗体, 循环会暂停下来...

在使用多线程以前, 让我们先简单修改一下程序:

function MyFun: Integer; 
var 
  i: Integer; 
begin 
  for i := 0 to 500000 do 
  begin 
    Form1.Canvas.Lock; 
    Form1.Canvas.TextOut(10, 10, IntToStr(i)); 
    Form1.Canvas.Unlock; 
  end; 
  Result := 0; 
end; 
 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  MyFun; 
end;

细数上面程序的变化:

1、首先这还不是多线程的, 也会让窗体假 "死" 一会;

2、把执行代码写在了一个函数里, 但这个函数不属于 TForm1 的方法, 所以使用 Canvas 是必须冠以名称(Form1);

3、既然是个函数, (不管是否必要)都应该有返回值;

4、使用了 500001 次 Lock 和 Unlock.

Canvas.Lock 好比在说: Canvas(绘图表面)正忙着呢, 其他想用 Canvas 的等会;

Canvas.Unlock : 用完了, 解锁!

在 Canvas 中使用 Lock 和 Unlock 是个好习惯, 在不使用多线程的情况下这无所谓, 但保不准哪天程序会扩展为多线程的; 我们现在学习多线程, 当然应该用.

在 Delphi 中使用多线程有两种方法: 调用 API、使用 TThread 类; 使用 API 的代码更简单,但Ken建议用 TThread 类. 以下是使用 API 的代码:

function MyFun(p: Pointer): Integer; stdcall; 
var 
  i: Integer; 
begin 
  for i := 0 to 500000 do 
  begin 
    Form1.Canvas.Lock; 
    Form1.Canvas.TextOut(10, 10, IntToStr(i)); 
    Form1.Canvas.Unlock; 
  end; 
  Result := 0; 
end; 
 
procedure TForm1.Button1Click(Sender: TObject); 
var 
  ID: THandle; 
begin 
  CreateThread(nil, 0, @MyFun, nil, 0, ID); 
end;

代码分析:

CreateThread 一个线程后, 算上原来的主线程, 这样程序就有两个线程、是标准的多线程了;

CreateThread 第三个参数是函数指针, 新线程建立后将立即执行该函数, 函数执行完毕, 系统将销毁此线程从而结束多线程的故事.

CreateThread 要使用的函数是系统级别的, 不能是某个类(譬如: TForm1)的方法, 并且有严格的格式(参数、返回值)要求, 不管你暂时是不是需要都必须按格式来;

因为是系统级调用, 还要缀上 stdcall, stdcall 是协调参数顺序的, 虽然这里只有一个参数没有顺序可言, 但这是使用系统函数的惯例.

CreateThread 还需要一个 var 参数来接受新建线程的 ID, 尽管暂时没用, 但这也是格式; 其他参数以后再说吧.

这样一个最简单的多线程程序就出来了, 咱们再用 TThread 类实现一次

type 
  TMyThread = class(TThread) 
  protected 
    procedure Execute; override; 
  end; 
 
procedure TMyThread.Execute; 
var 
  i: Integer; 
begin 
  FreeOnTerminate := True; {这可以让线程执行完毕后随即释放} 
  for i := 0 to 500000 do 
  begin 
    Form1.Canvas.Lock; 
    Form1.Canvas.TextOut(10, 10, IntToStr(i)); 
    Form1.Canvas.Unlock; 
  end; 
end; 
 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  TMyThread.Create(False); 
end;

TThread 类有一个抽象方法(Execute), 因而是个抽象类, 抽象类只能继承使用, 上面是继承为 TMyThread.

继承 TThread 主要就是实现抽象方法 Execute(把我们的代码写在里面), 等我们的 TMyThread 实例化后, 首先就会执行 Execute 方法中的代码.

按常规我们一般这样:

procedure TForm1.Button1Click(Sender: TObject); 
var 
  MyThread: TMyThread; 
begin 
  MyThread := TMyThread.Create(False); 
end;

因为 MyThread 变量在这里毫无用处(并且编译器还有提示), 所以不如直接写做 TMyThread.Create(False);

我们还可以轻松解决一个问题, 如果: TMyThread.Create(True) ?

这样线程建立后就不会立即调用 Execute, 可以在需要的时候再用 Resume 方法执行线程, 譬如:

procedure TForm1.Button1Click(Sender: TObject); 
var 
  MyThread: TMyThread; 
begin 
  MyThread := TMyThread.Create(True); 
  MyThread.Resume; 
end; 
 
//可简化为: 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  with TMyThread.Create(True) do Resume; 
end;

想进一步了解Delphi多线程编程,请继续看下一篇博文。

[转]Delphi多线程编程入门(一)的更多相关文章

  1. [转]Delphi多线程编程入门(二)——通过调用API实现多线程

    以下是一篇很值得看的关于Delphi多线程编程的文章,内容很全面,建议收藏. 一.入门 ㈠. function CreateThread(    lpThreadAttributes: Pointer ...

  2. 转载自~浮云比翼:Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

    Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)   介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可 ...

  3. 微博,and java 多线程编程 入门到精通 将cpu 的那个 张振华

    http://down.51cto.com/data/2263476  java 多线程编程 入门到精通  将cpu 的那个 张振华 多个用户可以同时用一个 vhost,但是vhost之间是隔离的. ...

  4. delphi 多线程编程

    开始本应该是一篇洋洋洒洒的文字, 不过我还是提倡先做起来, 在尝试中去理解.先试试这个: procedure TForm1.Button1Click(Sender: TObject); var i: ...

  5. 【转】Delphi多线程编程

    文章来源: http://liukun966123.my.gsdn.net/2004/10/22/4797/ Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书 ...

  6. C++多线程编程入门之经典实例

    多线程在编程中有相当重要的地位,我们在实际开发时或者找工作面试时总能遇到多线程的问题,对多线程的理解程度从一个侧面反映了程序员的编程水平. 其实C++语言本身并没有提供多线程机制,但Windows系统 ...

  7. Windows多线程编程入门

    标签(空格分隔): Windows multithread programming 多线程 并发 编程 背景知识 在开始学习多线程编程之前,先来学习下进程和线程 进程 进程是指具有一定独立功能的程序在 ...

  8. Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

    介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间, ...

  9. Delphi多线程编程--线程同步的方法(事件、互斥、信号、计时器)简介

    更详细的可以参考:http://www.cnblogs.com/xumenger/p/4450659.html 或者参考之后的博客 四个系统内核对象(事件.互斥.信号.计时器)都是线程同步的手段,从这 ...

随机推荐

  1. 【转】深入理解TextView实现Rich Text--在同一个TextView设置不同字体风格

    深入理解TextView实现Rich Text--在同一个TextView设置不同字体风格 作者: 字体:[增加 减小] 类型:转载   本篇文章是对Android中在同一个TextView中设置不同 ...

  2. 配置SQL Server Session方法

    以下过程是在Win 2003 SP2 + IIS 6.0, ASP.NET 2.0, SQL Server 2005下进行的. 1. 安装Session数据库到Framework目录 C:\WINDO ...

  3. Android中的CharSequence和String

    String 类代表字符串.Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例来实现. (这个没啥了解的吧,经常用 class)public interface ...

  4. PHP回调函数的几种用法

    PHP回调函数的实现方法 目录 前言      全局函数的回调      类静态函数的回调      对象的方法的回调      PHP事件模型(观察者模式)的实现思路    前言 最近在开发一个PH ...

  5. saltstack故障解决

    [root@saltstack_s ~]# salt '*' test.pingSalt request timed out. The master is not responding. If thi ...

  6. 根据子查询批量删除的sql语句

    delete  zi_provider_sa a where a.scheme_id in (select t.Id from zi_provider_scheme t where t.prov_id ...

  7. 第十篇 Replication:故障排除

    本篇文章是SQL Server Replication系列的第十篇,详细内容请参考原文. 复制故障排除是一项艰巨的任务.在任何复制设置中,都涉及到很多移动部件,而可用的工具并不总是很容易识别问题.Th ...

  8. SWIFT 闭包的简单使用

    import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: ...

  9. curl获得http响应码 302 和绑定host

    shell curl 取得HTTP返回的状态 获取状态码 curl -I -m 10 -o /dev/null -s -w %{http_code} www.baidu.com 获取时间   curl ...

  10. Oracle 12C -- 使用seed PDB创建新的pdb

    $ sqlplus '/as sysdba' SQL*Plus: Release Production on Tue Jun :: Copyright (c) , , Oracle. All righ ...