线程中调用主程序中的函数,而该函数又需调用VCL(比如Tmemory),会不会冲突 ( 积分: 50 )

  • 主题发起人 主题发起人 weismart
  • 开始时间 开始时间
W

weismart

Unregistered / Unconfirmed
GUEST, unregistred user!
线程中调用主程序中的函数,而该函数又需使用到一些VCL组件(比如Tmemory)。这样,在线程中调用主程序中的函数(定义在interface部分 )时会不会发生VCL冲突?说清楚一点,比如主程序中有一个function a, a中有调用TADOQuery1,执行过程当中因需要显示一些结果,采用form1.memory1.lines.add('要显示的信息')来完成,这种情况下,线程要调用function a来完成一些任务,但同一时间内,主程序中也有可能会调用adoquery1(其实form1.memory1也可能同时被使用到),这会不会产生VCL冲突呢。我习惯把一个大问题分成n个function(带返回值用于判断函数执行结果)其参数多用var(按地址传值),而且就放在主程序中,然后在线程中调用它们。不知delphi中该不该采用这办法?我是入门者,不过写出不少东西。只是水平还不行。特别是多线程我觉得乱乱的。
 
线程中调用主程序中的函数,而该函数又需使用到一些VCL组件(比如Tmemory)。这样,在线程中调用主程序中的函数(定义在interface部分 )时会不会发生VCL冲突?说清楚一点,比如主程序中有一个function a, a中有调用TADOQuery1,执行过程当中因需要显示一些结果,采用form1.memory1.lines.add('要显示的信息')来完成,这种情况下,线程要调用function a来完成一些任务,但同一时间内,主程序中也有可能会调用adoquery1(其实form1.memory1也可能同时被使用到),这会不会产生VCL冲突呢。我习惯把一个大问题分成n个function(带返回值用于判断函数执行结果)其参数多用var(按地址传值),而且就放在主程序中,然后在线程中调用它们。不知delphi中该不该采用这办法?我是入门者,不过写出不少东西。只是水平还不行。特别是多线程我觉得乱乱的。
 
VCL不是线程安全的,会有冲突,你可以先把和VCL打交道的代码封装成一个函数,然后用Synchronize执行这个函数就行了
 
有可能会冲突的,看具体的代码和对象是否线程安全.对一些界面不安全的组件.可以设计为一个无参数的方法,即TThreadMethod类型,然后在线程里用Synchronize来执行.对一与界面无关的安全保护,要可设定临界区进行保护.
 
主程序中的函数调用了memory
Function send_Alert(AlertMSG:string):integer;
begin
with Form1.ClientSocket_Alertdo
begin
Host:=gAlertIp;
Port:=gAlertPort;
ClientType:=ctBlocking;
try
Active :=true;
Socket.SendText(gAdminPhone+gAdminCtr+AlertMSG);
writelog(FormatDateTime('MM-DD HH:MM:SS',Now)+#9+gAdminPhone
+gAdminCtr+AlertMSG+#9+'报警操作成功!');
Form1.FailMemo.Lines.Add (FormatDateTime('MM-DD HH:MM:SS',Now)+#9+gAdminPhone
+gAdminCtr+AlertMSG+#9+'报警操作成功!');
except on E:Exceptiondo
begin
writelog(FormatDateTime('MM-DD HH:MM:SS',Now)+#9+gAdminPhone
+gAdminCtr+AlertMSG+#9+'报警操作失败,原因:'+e.message);
Form1.FailMemo.Lines.Add(FormatDateTime('MM-DD HH:MM:SS',Now)+#9+gAdminPhone
+gAdminCtr+AlertMSG+#9+'报警操作失败,原因:'+e.message);
end;
end;
Active :=False;
end;
Result:=0;
end;
以上是我在主进程中的函数(像这样的函数我一共写了10来个),然后在线程中调用
我的线程如下:
procedure SMGThread.Execute;
begin
Form1.PauseBT.Enabled:=True;
Form1.SMGTestBT.Enabled:=False;
CoInitialize(nil);
get_SMGArray(gSMG_Array,gSMG_Count);
if (gSMG_Count<>0) then
begin
sendsmg(gsmg_array,gsmg_count);//以下这几个都是主程序中定义的函数
del_smgbuffer(gsmg_array,gsmg_count);//均调用memory1来显示函数执行过程的情况
insertlogtable(gsmg_array,gsmg_count);
end
else
sleep(gNoSMGSleepTime);
CoUnInitialize;
Form1.PauseBT.Enabled:=False;
Form1.ResumeBT.Enabled:=False;
Form1.SMGTestBT.Enabled:=True;
Form1.statusbar1.Panels[2].Text:='线程发呆...';
sleep(gPerSQLTime);
end;
线程中的那几个函数都调用到form1.memo1 /form1.adoquery/ form1.adoconnection等。
可能是受C语言影响,我总爱把问题析分成函数,最后通过一连串调用(用var方式传参数)后解决问题。但这样又担心VCL冲突,找书看,有关多线程方面的太少了。没有专业分析的。难道delphi在线程中调用外部函数就这么难?任何一个线程只要用到主进程中的VCL(包括调用使用了VCL的函数)都非得把vcl当成传递给线程的参数,并且一定得进行同步??外部的函数中只要它是调用了某个vcl(就算最简单的memory)也不行。
 
使用和界面无关的组件和对象一般不用考虑线程安全问题,
如: 字符串,TStrings, TGraphic, TComponent
但要考虑公共资源的互斥同步访问,
同时还要看所使用的类库是否支持多线程
 
同意Beyondbill,
Sychronize方法是把其参数程序放到主线程中来执行,是防止线程冲突的一种巧妙的解决办法
 
以下代码纯属测试,写得有点乱,请高手帮我找问题。
毕业设计中遇到多线程与数据库问题。
怀疑多线程中动态生成的N个Tadoquery共享主Form1中的Tadoconnection时会有问题,于是写测试代码来测试,结果确实如此,但又想不通测试代码为什么出错,请各位高手帮忙看一下,付上源代码如下
//-----------主窗体放置一TADOconnection /Tmemo/Tbutton三个控件
//其中Tmemo用于显示从数据库中取得的时间
//Tbutton 用于生成n个的查询动作。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, DB, ADODB,unit2;
type
TForm1 = class(TForm)
Button1: TButton;
ADOConnection1: TADOConnection;
//TadoConnection作为线程中动态生成
//TadoQuery的公用连接///
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

TestThread:TMyThread;
//创建n个一模一样的线程
TestThread2:tmythread;
//与上面的一模一样///
TEstThread3:Tmythread;
TestThread4:Tmythread;
TestThread5:Tmythread;
TEstThread6:Tmythread;
TestThread7:Tmythread;
TestThread8:Tmythread;
implementation

{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
//点击button1瞬间生成8个线程,动态生成8个adoquery,它们共享form1的
//adoconnection1,点击button1到第七次时出错了,每次都是这样的
TestThread:=TMyThread.Create(adoconnection1,memo1);
Testthread2:=TMyThread.Create(adoconnection1,memo1);
TEstThread3:=Tmythread.Create(adoconnection1,memo1);
TestThread4:=Tmythread.Create(adoconnection1,memo1);
TestThread5:=Tmythread.Create(adoconnection1,memo1);
TEstThread6:=Tmythread.Create(adoconnection1,memo1);
TestThread7:=Tmythread.Create(adoconnection1,memo1);
TestThread8:=Tmythread.Create(adoconnection1,memo1);
end;

end.

//----------------线程单元---------------------------//
unit Unit2;
interface
uses
Classes,DB,StdCtrls, ADODB,ActiveX;
type
TMyThread = class(TThread)
private
FAdoQuery:TADOQuery;
FMemory:Tmemo;
FADOConnection:TADOConnection;
protected
procedure Execute;
override;
public
constructor Create(adoconnection:TadoConnection;memory:TMemo);
destructor destroy;
procedure runsql;
end;

implementation
constructor TMyThread.Create(adoconnection: TadoConnection;
memory: TMemo);
begin
CoInitialize(nil);
//初始化ado
inherited create(false);
Fadoconnection:=adoconnection;
Fadoquery:=Tadoquery.Create(nil);
fadoquery.Connection:=fadoconnection;
Fmemory:=memory;
FreeOnTerminate:=True;
end;

destructor TMyThread.destroy;
begin
Fadoquery.Free;
CoUninitialize;
inherited destroy;
end;

procedure TMyThread.Execute;
begin
synchronize(runsql);
end;

procedure TMyThread.runsql;
var
datetimestr:string;
begin
datetimestr:= 'select to_char(sysdate,'''+'yyyymmdd HH24:MI:SS'''+') from dual';
if not Fadoconnection.Connected then
try
FadoConnection.Open;

except
Fmemory.Lines.Add('opendb error');
end;
try
////////------不要了 Fadoquery:=Tadoquery.Create(nil);
Fadoquery.Connection:=fadoconnection;
fadoquery.SQL.Clear ;
fadoquery.Close;
fadoquery.SQL.Add(datetimestr);
fadoquery.open ;
datetimestr:=fadoquery.Fields[0].AsString;
fmemory.Lines.Add(datetimestr);
//显示时间
except
Fmemory.Lines.Add('sql error');
end;
end;

end.

按button1第七次后出错,结果如下
project project1.exe
ora-01000:超出打出光标最大值。
Memo1
20050421 21:04:54
20050421 21:04:54
20050421 21:04:54
20050421 21:04:54
20050421 21:04:54
20050421 21:04:54
20050421 21:04:54
20050421 21:04:54
20050421 21:04:55
20050421 21:04:55
20050421 21:04:55
20050421 21:04:55
20050421 21:04:55
20050421 21:04:55
20050421 21:04:55
20050421 21:04:55
20050421 21:04:56
20050421 21:04:56
20050421 21:04:56
20050421 21:04:56
20050421 21:04:56
20050421 21:04:56
20050421 21:04:56
20050421 21:04:56
20050421 21:04:56
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:57
20050421 21:04:58
20050421 21:04:58
20050421 21:04:58
20050421 21:04:58
20050421 21:04:58
20050421 21:04:58
20050421 21:04:58
20050421 21:04:58
20050421 21:04:59
20050421 21:04:59
sql error
sql error
sql error
sql error
sql error
sql error
 
ORA-01000: 表示已经达到一个进程打开的最大游标数
open 读出数据后就close看看
 
试过了,不行的。听说左轻候的离线游览器就是共享adoconnection的,我发贴子求源代码链接,但没有人理会我,可怜,想哭。。。。。。。。。。
 
在多线程的时候调用vcl组件肯定会有资源冲突,只要使用的时候保持线程的同步和互斥就可以了,可以使用临界区或者内存映射文件等来解决。建议你这种程序把数据库连接字符串传入查询线程,在线程内部保持一个数据库连接。
 
因对数据库只允许一个长连接。也就是说,只允许我的程序有一个连接连到oracle数据库。
但我又是多线程。我试过了,如果用Tsession +Tdatabase+Tquery是不会有问题的,但现在哪里还有人用DBE odbc等之类的东西。
 
自已测试过程一不小心解决了
fmemory.Lines.Add(datetimestr);
//显示时间
//fadoquery.Close;
加上这句没什么用处
fadoconnection.Close ;//加上这句后问题解决了,只是想不通为什么这样
//因为长连接是可以保留的,为什么非要关闭才没有问题
//注意了,如果采用动态生成Tadoconnection也得加上这句否则点击button1 37次后
//也有同样的问题。同样也是想不通,我测试时动态生成的Tadoconnection都Free了,皮之不存了,close这根毛怎么还得拔呢
//想不通
 
多人接受答案了。
 
后退
顶部