第十二章并发共享访问编程及其它12.1多用户环境及多用户操作函数、命令.................................................................................112.2使用数据工作期.....................................................................................................................512.3用缓冲(Buffering)访问共享数据.......................................................................................712.4用事务处理(Transaction)管理更新数据.........................................................................1212.5Internet与VFP的ActiveDocument...............................................................................16如果建立的应用程序在网络环境中的多台计算机上运行,或者一个表单的多个实例对相同数据进行访问,这就要进行共享访问程序设计。共享访问不仅能为用户使用数据和数据共享提供更有效的方法,而且在必要时可对访问进行限制。VFP具有对数据的共享或独占访问;锁定选项;数据工作期;记录缓冲和表缓冲以及事务处理等功能。尽管这些功能主要用在共享环境里,但在单用户环境下也可以使用。12.1多用户环境及多用户操作函数、命令FoxPro2.5以上的FoxPro和VFP都是多用户版本,没有单用户版本了。在一个网络环境(例如Novell网文件服务器)中,一个工作站(Workstation)上的用户试图访问一个正被另外的工作站用户访问的记录或文件就会产生碰撞(Collision)。当这种情况出现时,如果多于一个用户打算修改这个记录,则会导致损坏数据的结果。于是便出现了象APPEND、APPENDFROM、ALTERTABLE、INDEX、INSERT、JOIN和UPDATE、UPDATE-SQL等能够自动加锁(隐含加锁)整个表,象APPENDBLANK、APPENFROMARRAY、INSERT-SQL等能自动加锁表头,象APPENDMEMO、DELETE、DELETE-SQL、GATHER、BROWSE、CHANGE、EDIT、MODIFYMEMO、READ、RECALL、REPLACE、SHOWGETS等能自动加锁记录的命令(CURSORSETPROP()取决于缓冲进行自动加锁),防止其它工作站破坏用户正访问的数据。但如果不及时解锁,又可能出现死锁(DeadlyEmbrace)的情况,即:当一个用户锁定了一个记录并希望访问一个正被第二个用户加锁了的记录,或反之,第二个用户正试图访问第一个用户锁定了的记录,等等。死锁的出现是很少的,但随着一个记录加锁的时间的增加,它的可能性也在增加。VFP提供了如下一些人工明显文件加锁、人工明显记录加锁和解锁以及捕捉错误的函数和命令,可有效地解决上述问题(要求只读访问一个表的命令,不用文件加锁和记录加锁)。1.表文件加锁的函数FLOCK()格式:FLOCK([工作区号或别名])说明:该函数试图将当前或指定工作区的表文件锁定(锁定全部记录)。如果加锁成功,返回.T.(真),这样可读写该表,其它用户只能读不能写。如果加锁不成功,则返回.F.(假),表示这个表或表中的一个记录早已被其它用户锁定了。函数返回类型逻辑型。用户可以根据情况用UNLOCK、USE、CLEARALL或者CLOSEDATABASE等命令来关闭表。在缺省情况下,FLOCK()只对一个表进行一次锁定尝试。用SETREPROCESS,当第一次尝试失败时自动地再次锁定这个表。SETREPROCES确定尝试的次数和再次尝试的间隔时间(详见VFP软件的Help)。用户还可以完全独占地加锁表,防止其他用户访问它(即,将其他用户的只读访问权也剥夺掉),用SETEXCLUSIVEON和USE表EXCLUSIVE命令便可独占表。在前面章节,介绍表索引时,已这样用过独占设置命令。2.表记录加锁的函数RLOCK()格式:RLOCK([工作区号或别名]|[,[字符表达式列表][,工作区号或别名]])说明:VFP的RLOCK()函数可锁定多个记录。其中[字符表达式]任选项指定一个或者多个记录号,并用逗号将它们分开,这样RLOCK()函数将试着给这几个记录加锁。例如,要给表中的前5个记录加锁,字符表达式就必须含有1,2,3,4,5。[字符表达式]不选,只给当前记录加锁。用户要给多个记录加锁必须将SETMULTILOCKS设置为ON,并且要给出记录所在工作区号或表的别名。用户还可将记录指针移到相应记录,然后给出RLOCK()函数,重复以上过程给其它记录加锁,同样可达到给多个记录加锁的目的。在VFP中,可将记录号设置为0,这样用户可给表头加锁,锁定库头时间一定要短,因为一旦锁定了库头,其他用户便不能在表中添加或删除记录了,但可修改字段内的数据。表锁定命令锁定整个表,比表头锁定命令更严厉。当前或指定工作区表中由字符表达式列表所指定的记录都加上锁,RLOCK()将返回.T.(真),如果表达式列表中的一个或者多个记录不能被锁定,RLOCK将返回.F.(假),并且不给任何一个记录加锁。在上述两种情况下,仍然保留以前已经存在的记录锁定。函数返回类型逻辑型。在网络中,只有加锁者本人才能对加锁记录进行读写操作,这些记录对于其他用户来说是只读的。记录锁定只能由加锁者本人打开。用户可以通过给出UNLOCK命令,关闭表或者退出VFP系统都能够给记录解锁。命令UNLOCK[RECORD记录号数值表达式][IN工作区号或别名][ALL]可以用来解开当前工作区、指定工作区的指定记录或所有工作区的记录锁定。一般RLOCK()为记录进行一次加锁尝试,多次加锁尝试的处理与FLOCK()的多次尝试相同。LOCK()与RLOCK()完全等同。3.检测ONERROR语句捕捉的出错号ERROR()的函数格式:ERROR()说明:ERROR()返回最近的一个错误号。在ONERROR命令被激活的情况下,ERROR()返回一个非0值。函数返回类型数值型。当程序执行时遇到了错误(如隐含加锁失败等),可用ONERROR程序中的ERROR()函数查明错误的类型,用MESSAGE()函数可返回相应的错误信息。ERROR()返回值可用RETURN或RETRY来复位,复位为0。4.检测ONERROR语句捕捉的出错号对应的出错信息的函数MESSAGE()格式:MESSAGE([1])说明:此函数功能是:以字符串的形式返回当前的错误信息。任选项[1],表示可返回引起错误的源程序命令行内容。如果此程序代码不能用,则MESSAGE(1)将返回以下几种形式之一。(1)如果此行是宏替换的,返回整个程序行。(2)如果此行包含的命令没有任何附加的子句时,返回此命令。(3)如果此行包含有命令和附加的子句时,将返回此命令和后面三个小点(…)。本函数返回类型为字符型。和ERROR()函数不一样,MESSAGE()不能由RETURN或者RETRY重新设置。5.关于多用户的系统的函数SYS()格式:SYS(数值表达式)说明:详见4.7节。另外,还有几个重要的与多用户函数相关的多用户命令:如SETPRINTERTO可设置网络打印等;RETRY命令可将控制返回给调用的程序并且在调用不是重新执行最后一行(RETRY)外,RETRY命令类似于RETURN(返子程序)命令。当一组命令必须重复直到一个确定的条件为真时,RETRY命令特别有用。比如RETRY在错误处理程序中是很用的,在给记录或者文件加锁的时候也是这样,RETRY频繁地执行同一个命令,直到成功地给记录或者文件加上锁为止。用户可以使用SETREPROCESS来控制记录或者文件加锁函数的执行。在大多数网络环境中,使用SETREPROCESS更为可取。例12.1:使用APPENDBLANK隐含加锁文件头和在与其他用户来“碰撞”时进行捕捉错误的处理。SETEXCLUSIVEOFF&&设置为共享环境ONERRORDOfixUSEVideoAPPENDBLANKONERROR***下面的Fix.PRG是处理上面错误的子程序IFERROR()=108&&108错误是文件被另外的用户在用@23,0SAY请等待去添加一个记录RETRYELSE@23,0SAY请去咨询你的系统管理员,关于这个错误是什么原因+MESSAGE()WAITENDIF例12.2:共享表情况下的文件加锁。SETEXCLUSIVEOFFSETREPROCESSTO0USEVideoIFFLOCK()REPLACEALLCost_rentWITHCoxt_rent+1.00UNLOCKELSE@22,0SAYFileisusebyanother。ENDIF在上面例子中,表Video为共享访问而打开。FLOCK()函数与IF…ELSE…ENDIF结构结合去确定锁定状态。如果文件由于没有另外的用户使用而加锁成功,则执行REPLACEALL命令,并且租用一个录像(Video)带的价钱上涨1.00美元。完成REPLACEALL操作后,执行UNLOCK命令去解除文件锁定。如果文件由于另外的用户已使用而加锁失败的话,则出现一个“文件被另外的人使用”的屏幕信息。例12.3:记录加锁。SETEXCLUSIVEOFFUSEVideoLOCATEFORCost_rint=25.00DOWHILE.NOT.LOCK().and.TIME<1000TIME=TIME+1ENDDOIFTIME<1000REPLACECost_rentWITHCost_rent+5.00ELSE@23,OSAY记录不能加锁,稍后再试。ENDIF上面的例子中,LOCATE命令找到Video库中第一个Cost_rint=25.00元的记录,并用Lock()函数与DOWHILE…ENDDO循环结构结合去尝试锁定该记录。如果加锁不成功,则计次数变量TIME加1,再循环去加锁,直到LOCK()为真(.T.)即.NOT.LOCK()为假(.F.)时跳出循环(或者加了1000次也不成功,即TIME>1000,也跳出循环)。若TIME<1000时加锁成功,则执行将该记录租金字段加5.00元的REPLACE语句。否则在屏幕上显示“记录不能加锁,稍后再试”的信息。12.2使用数据工作期为确保共享环境中的每个用户都具有安全、正确的环境,确保表单的多个实例能独立操作,VFP提供了数据工作期。数据工作期是对当前动态工作环境的描述。可以将数据工作期看成是一个小型的数据环境,这个环境是在一台机器上运行的一个开放的VFP工作期。每个数据工作期包括:(1)表单的数据环境中各项的备份。(2)表示打开的表、索引和关系的临时表。请考虑当在多用户应用程序的各自工作站同时打开相同的表单时,会发生什么,这样可以很容易理解数据工作期的概念。这种情况下,每个工作站运行一个独立的VFP工作期,因此有自己的工作区设置以及自己表示打开的表、索引和关系的临时表。但是,如果你在一个机器上,同一个VFP工作期,打开单个项目中的同一个表单的多个实例,则这些表单共享默认的数据工作期,它代表了单个的动态工作环境。在同一个VFP工作期中打开的表单的每一个实例使用相同的工作区设置,并且在一个表单实例中移动工作区中的记录指针会自动影响相同表单的其他实例。12.2.1使用私有数据工作期如果想更多地控制表单的多个实例,可以使用私有数据工作期。当表单使用私有数据工作期时,VFP为应用程序创建的表单、表单集或