当有多个线程的时候,经常需要去同步这些线程以访问同一个数据或资源。
例如,假设有一个程序,其中一个线程用于把文件读到内存,而另一个线程用于统计文件的字符数。当然,在整个文件调入内存之前,统计它的计数是没有意义的。但是,由于每个操作都有自己的线程,操作系统会把两个线程当做是互不相干的任务分别执行,这样就可能在没有把整个文件装入内存时统计字数。为解决此问题,你必须使两个线程同步工作
存在一些线程同步地址的问题,Win 32 提供了许多线程同步的方式。这里将会讲到:临界区、互斥、信号量和事件
为了检验这些技术,首先来看一个需要线程同步解决的问题。假设有一个整数数组,需要按照升序赋初值。现在要在第一遍把这个数组赋初值为1~128,第二遍将此数组赋初值为128~255,然后结果显示在列表中。要用两个线程来分别进行初始化。下面的代码给出了没有做线程同步的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
unit Main; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMainForm = class (TForm) Button1: TButton; ListBox1: TListBox; procedure Button1Click(Sender: TObject); private procedure ThreadsDone(Sender: TObject); end ; TFooThread = class (TThread) protected procedure Execute; override; end ; var MainForm: TMainForm; implementation {$R *.DFM} const MaxSize = 128 ; var NextNumber: Integer = 0 ; DoneFlags: Integer = 0 ; GlobalArray: array [ 1.. MaxSize] of Integer ; function GetNextNumber: Integer ; begin Result:= NextNumber; //return global var Inc(NextNumber); //inc global var end ; procedure TFooThread . Execute; var i: Integer ; begin OnTerminate:= MainForm . ThreadsDone; for i:= 1 to MazSize do begin GlobalArray[i]:= GetNextNumber; Sleep( 5 ); end ; end ; procedure TMainForm . ThreadsDone(Sender: TObject); var i: Integer ; begin Inc(DoneFlags); if DoneFlags = 2 then for i:= 1 to MaxSize do ListBox1 . Items . Add(IntToStr(GlobalArray[i])); //注意ListBox的使用,并看下面编译运行的效果图 end ; procedure TMainForm . Button1Click(Sender: TObject); begin TFooThread . Create( False ); //创建一个新的线程 TFooThread . Create(Flase); //再创建一个新的线程 end ; end . |
因为两个线程同时运行,同一个数组在两个线程中被初始化会出现什么呢?你可以看下面的截图

这个问题的解决方案是:当两个线程访问这个全局数组时,为防止它们同时执行,需要使用线程的同步。这样,你就会得到一组合理的数值
1.临界区
临界区是一种最直接的线程同步方法。所谓临界区,就是一次只能有一个线程来执行的一段代码。如果把初始化数组的代码放在临界区内,那么另一个线程在第一个线程处理完之前是不会被执行的。
在使用临界区之前,必须使用 InitializeCriticalSection()过程初始化它,其声明如下
1 |
procedure InitializeCriticalSection( var
|