一个很有用的问题,可又不容易实现(线程)。(300分)

  • 主题发起人 主题发起人 maming
  • 开始时间 开始时间
M

maming

Unregistered / Unconfirmed
GUEST, unregistred user!
我做三层的应用时,在LAN中很好,联接数据库,打开数据表,保存数据都很快,
根本感觉不到慢,几十个客户端也不会有问题。可internet上就问题来,不是程
序出错,而是在联接数据库,打开数据表,保存数据都很慢,客户端在这三种操
作时那界面就象死机,所以产生了以下想法,能不能在上面三种操作时创建一个
线程在后台执行,前面界面不死,当然要控制不能做其它的操作,就显示一个正在(联接,打开,保存)数据的窗口,整个程序还能最小化。甚至还要能通过这个显示的窗口中止当前的操作(联接,打开,保存);

就象在263玩游戏一样,后台的数据处理对前台的界面没有影响。可以各位大
虾早有实现了的,可否贴出来。重金酬谢!我做了数据联接的,可没有实现上面那种效果。

过程如下;
procedure threadconnect(socketconnect:Tsocketconnect);
begin
try
... //这里执行联接数据库和显示系统正在联接远程数据库。
except
end;
end;

procedure threadconnect(clientdataset:Tclientdataset;bopendataset:boolean);
begin
try
if bopendataset
then
...//这里执行打开和显示系统正在打开远程数据表。
else
...//这里执行保存和显示系统正保存数据到远程数据库。
except
end;
end;

这样的话,整个应用程序中都使用这两个过程进行操作,那么这个系统将是多么
有意思,客户用起来也就不会感到死机了。
 
大虾们,多多指教
 
没有环境测试,如果adoconnection.connected=true 很快而adotable.open 很漫的话,
try it
adotable.executeoption=。。
 
现在问题是不管联接速度快与慢,因为客户数一多的话总会有慢的时候,而我要实现的是
客户在实现数据联接时程序要能动,也就是可以最小化或是拖动窗口,或是点击其它的地方。
 
我这样做了又出现了新的问题,
function mconnect(p:pointer):longint;stdcall;
begin
form1.socketconnect1.open;
reuslt:=1;
end;

procedure Tform1.button1click(sender:Tobject);
var mthreadid: DWORD;
begin
Try
Createthread(nil,0,@mconnect,nil,0,mthreadid);
if mthreadid=0
then
showmessage('no thread');
closehandle(mthreadid);
except
end;
end;

实现了用户界面不死,然而新问题又来了,用clientdataset1(是与socketdonnect1联接的)
去打开时报"class EOleException with message '意外错误'."错误,我的天,我原来用Tthread做时也出现这样的错误,我快没有办法了。
 
我这样做的时候又有一个问题;
TThreadQuery1 = class(TThread) { 声明线程类 }
private
Fclientdataset: Tclientdataset;
procedure syconnect;
protected
procedure Execute;
override;{ 执行线程的方法 SocketConnection: TSocketConnection;
}
public
constructor Create(clientdataset: Tclientdataset);
virtual;
{ 线程构造器 }
end;


procedure TThreadQuery1.syconnect;
begin
{这样的话就可以实现界面不死,但不能操作它也不能用与它相联的socketconnect}
FClientDataSet.Open;
{用Form1.ClientDataSet1.Open;这样的话界面就会死但可以使用clientdataset1,和
其它的clientdataset也就是说那个与socketconnect就是正常的联接了}
end;

procedure TThreadQuery1.Execute;{ 执行线程的方法 }
begin
try
Synchronize(syconnect);
except
end;
end;

{ 线程查询类的构造器 }
constructor TThreadQuery1.Create(clientdataset:Tclientdataset);
begin
Fclientdataset := clientdataset;
inherited Create(False);
FreeOnTerminate := True;
end;

procedure Tform1.button1click(sender:Tobject);
begin
threadquery1.create(form1.clientdataset1);
end;
 
这是第二种方法:
如果你碰到那些很早就使用电脑的用户?他们的机器很早,可电脑水平很高。
所以要求高得很,不过这个问题我觉得很正常,我也很想实现这种技术
请试用下面的代码,就可以看到效果,可会在退出时出错,这就是我的目的function mconnect(p:pointer):longint;stdcall;
begin
//这里如果用了vcl就在退出时出错;
form1.scoketconnect1.open;
end;
procedure TForm1.Button1click(sender: Tobject);
var mthreadid: Dword;
begin
createthread(nil,0,@mconnect,nil,0,mthreadid);
if mthreadid=0
then
showmessage('no thread');
end;

而当我要用clientdataset1去联接scoketconnect1时就出错了,不能打开数据。
 
我想:
1、如果在 thread 中打开 ClientDataSet, 应该先 disablecontrols, 断开与 UI 控件的
联系,Open 之后再 enableControls。ClientDataSet 要用到 COM,可能需要在 thead 的
execute 中用 CoInitialize(nil);
try ... finally CoUninitialize;
end;
将操作包含
起来。
2、如果在 thread 中打开 SocketConnection,试试先把 clientDataSet 的 RemoteServer
等设为 nil, 断开 connection 与 dataset 之间的联系,打开后再使它们关联。
SocketConection 也要用到 COM 的,试试象上面说的用那个 try ... finally 块初始化,
清理 COM 库运行环境。
不行的话可以试试在 thread 中动态地创建 connection ,再传给主线程。
 
to bbkxjy:
1、这样不成功,在Execute中用Fclientdataset退出时还会出现非法错误。
2、也不成功,可能是我的创建代码有问题:
在execute中:
try
CoInitialize(nil);
finally
CoUninitialize;
end;

FDcomconnect := TDcomconnect.create(application);
FDcomconnect.servername := 'server.servermis';
FDcomconnect.open;
..
一样报那个co什么没有初始化

 
试试:
procedure TMyThread.Execute;
begin
CoInitialize(nil);
try
FClientDataSet.Open;
//或 Connection.Open;
在这里打开连结或数据集
finally
CoUninitialize;
end;

end;
 
只有FClientDataSet.Open 放在execute中才可以让界面不死,可用上面的方法也是一样,
其它的clientdataset不能用FClientDataSet所对应的socketconnect;
bbkxjy大侠,不知有没有时间帮我试试,我快没辙了;
 
to maming:
关键是 IAppServer 接口在 Apartment 线程中传递时必须先经过 Marshal,比如 Connection
在某一 Thread 中 Open 的,则返回的应用程序服务器的 IAppserver 接口指针只能在该 thread
中使用。如主 VCL 线程中的 ClientDataSet 要使用该 connection 得到的 AppServer 接口,
则必须在该 thread 中 Marshal 这个 AppServer 接口指针,然后在主线程中 UnMarshal 出
该接口再赋给 ClientDataSet 的 AppServer 属性。这样子是很麻烦的。我对 D5 带的 Adhoc
Demo 改了一下,基本可以进行在后台线程中打开 DCOMConnection 和 ClientDataSet,如果
需要可以发给你。我曾试图修改 TDispatchConnection 的 SetAppServer 和 GetAppServer 等
方法,希望实现自动对 AppServer 接口指针 Marshal/UnMarshal,但没成功.
 
to bbkxjy:先谢谢你的热情相助,这样的问题只有几个人才愿意去试,可能其它的高手认为
过于简单或是认为没有这个必要,但我却是相当的想实现,好在有你这样的高手帮忙,否则,
我想我只有放弃了,你把那个你改过的DEMO发过来吧!非常感谢你!有什么新的进展我一定第
一个通知你。:)
mailto: maming_hn@163.net
 
to maming:
已发过去。请查收。我也想知道更好的办法。
 
maming,俺再给你添点分做奖金。
http://www.delphibbs.com/delphibbs/dispq.asp?LID=548625
 
To bbkxjy:
已收到,谢谢你的帮助,我看了一遍难度相当的大,而且这样的话,可能客户端也在bde了
不过可以改。
如果有多个clientdataset就较麻烦了对于那个clientdata的关闭事件,你的说明中指出要
更改DBCLIENT.PAS,后来我在close之前加了一句,clientdata.appserver := nil;就没有出现
错误了。
不知你看过ibevent的例子没有,我看了一下,那个例子很有意思,而且对于用interbase
数据库的应用很有好处,但我没有完全理解其中的作用,因为我认为那个ibevent可能是要实现
实时数据库的功能。如果真是那样,那么interbase将会有很大的发展前途,我有时间的话,
我想我会设计一个方案,将我做的系统做成实时的,因为是三层的应用,应该很好改。你如果
看过了可能会有更深的理解,可否谈谈你的看法。
 
to maming:
你好,mail 已收到,但我没装 OICQ,暂时在这里说吧。你说的"客户端也在BDE" 指的是
我在 Client 中用了 RDM 吗?记得并没有用什么 BDE Datacontrols 啊。用 RDM 是为了利用
它的 ApartmentThread ,一般在 AppServer 中,客户请求一个连接,ComponentClassFactory
就会创建一个新的 Apartmentthread,在该 thread 中创建一个 RDM,RDM 实现了 IAppServer
接口让 Client 使用。这个具体你可以看 VCLCom.pas。ApartmentThread 中有一个消息循环,
保证 RDM 在引用计数为零时被释放并结束线程。在 Client 中,如果你这样:
procedure TMythread.Execute;
begin
CoInitailize(nil);
try
connection.Open;
//这里向 AppServer 请求并得到一个 IAppServer 接口指针。
finally
CoUninitialize;
//这里清理 COM 库运行环境,则上面的 IAppServer 接口将又被释放
end;
end;
则 Mythread 结束后实际 Connection 的 AppServer 接口已被释放了。因此你的线程必须
有一个循环,保证得到的接口不会马上被释放,在接口已不用的时候才退出线程。这是我
在 Client 中借用 RMD 的原因。
你关于这问题的另一帖子我也看过,Alter 的 demo 下来,原来是 Dan Miser 的 Midas
Essential Pack,在 www.distribucon.com 上,还有其他一些资料的。这个 demo 中思路
差不多的,将主线程中的 IProvider 接口指针传入 thread 中供 cds 使用。那是 Delphi4,
cds 用的 IProvider 接口。
 
to bbkxjy: 感谢你的解释,这让我理解了不少,我试着用这种方法去做,但愿我能做成
一个很好用的方法,就象开始我提的那样,先不结束这个问题,这样好交流一些,周未不知
会不会很忙,我去尽快的实现,周未我不上班所以不能上网。
 
问题基本上解决了,谢谢大家,过几天我把源程序公布出来。
 
后退
顶部