有关线程和函数指针?

amiao 2003-08-20 03:38:46
代码看起来可能有点烦,不过都是些简单的code,请高手指点迷津!
线程同步类中的published方法时,Receive为inaccessible value,怎样解决?
分数可以再加.

部分代码如下:

...

type
TForm1 = class(TForm)
...
end;

//定义函数指针对象类型
TFunc = function: Integer of Object;

//定义接收线程
TReceiveThread = class(TThread)
private
FFunc: TFunc;
protected
procedure SynKernel;
procedure Execute; override;
end;

//定义核心类
TKernel = class
public
Receive: TList; //接收信息缓冲
function IsCreate: Integer;
published
destructor Destroy; override;
function SynReceive: Integer; //线程同步核心类方法
end;

var
Form1: TForm1;
Kernel: TKernel;
Re: TReceiveThread;

implementation

{$R *.dfm}

{====================TReceiveThread====================}
procedure TReceiveThread.SynKernel;
begin
TMethod(FFunc).Code := TKernel.MethodAddress('SynReceive');
if Assigned(TMethod(FFunc).Code) then
if FFunc=0 then ShowMessage('not assign')
else ShowMessage('assign');
end;

procedure TReceiveThread.Execute;
begin
FreeOnTerminate := True;
// while not Terminated do
Synchronize(SynKernel);
end;

{====================TKernel====================}
function TKernel.SynReceive: Integer;
begin
Result := 0;
//线程同步类中的published方法时,Receive为inaccessible value,怎样解决?
if Receive=nil then Exit;
//同样,这句代码也不行
// if not Assigned(Receive) then Exit;
Result := 1;
end;

constructor TKernel.Create;
begin
inherited;
Receive := TList.Create;
end;

destructor TKernel.Destroy;
begin
Receive.Free;
inherited;
end;

function TKernel.IsCreate: Integer;
begin
if Receive=nil then
Result := 0
else Result := 1;
end;


{====================TForm1====================}
procedure TForm1.FormCreate(Sender: TObject);
begin
Kernel := TKernel.Create;

Re := TReceiveThread.Create(False);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
Kernel.Destroy;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
//这里的调用一切正常
case Kernel.IsCreate of
0: Caption := 'not assign';
1: Caption := 'assign, no data';
end;
end;
...全文
51 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
copy_paste 2003-08-21
  • 打赏
  • 举报
回复
TMethod只要传来Code值,即可以运行,不一定需要Data值,
如下:

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
FInt2: Integer;
published
function GetInt1: Integer;
function GetInt2: Integer;
end;

TFunc = function: Integer of object;

TChild = class
private
FFunc: TFunc;
public
constructor Create;
end;

implementation

{ TForm1 }

function TForm1.GetInt1: Integer;
begin
Result := 1;
end;

function TForm1.GetInt2: Integer;
begin
Result := FInt2;
end;

{ TChild }

constructor TChild.Create;
begin
inherited Create;
TMethod(FFunc).Code := TForm1.MethodAddress('GetInt1');
if Assigned(FFunc) then
ShowMessage(IntToStr(FFunc));


{ 这是类实例的GetInt2方法指针 }
TMethod(FFunc).Code := Form1.MethodAddress('GetInt2');
if Assigned(FFunc) then
ShowMessage(IntToStr(FFunc));

{ 这是类的GetInt2方法指针 }
TMethod(FFunc).Code := TForm1.MethodAddress('GetInt2');
if Assigned(FFunc) then
ShowMessage(IntToStr(FFunc));
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
TChild.Create.Free;
end;
lxpbuaa 2003-08-21
  • 打赏
  • 举报
回复
如果不能解决TMethod(FFunc).Data = nil 的问题,即使使用:
TMethod(FFunc).Code := Kernel.MethodAddress('SynReceive');
也是无济于事。
但如果直接使用:
FFunc := Kernel.SynReceive;
那么又没有体现类的封装性,因此像上面我说的那样重载线程类的构造器,应该是个比较好的解决方案。

—————————————————————————————————
宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
—————————————————————————————————
amiao 2003-08-21
  • 打赏
  • 举报
回复
明天结贴...
amiao 2003-08-21
  • 打赏
  • 举报
回复
知道了,谢谢
amiao 2003-08-21
  • 打赏
  • 举报
回复
谢谢楼上lxpbuaa兄,OOP我还是门外汉:(

顺便问句,怎么加分?
lxpbuaa 2003-08-21
  • 打赏
  • 举报
回复
copy_paste(木石三) (★★)兄:
TKernel.MethodAddress('SynReceive');是正确的,因为方法的地址只是和类有关而与实例无关,因此TObject.MethodAddress被定义了一个类方法,不一定只有实例采用调用它。问题的关键是普通方法FFunc需要由实例调用,而它的代码里没有指定其Data(即实例),因此调用失败。至于其他的一些问题,如定义了一些不必要的全局变量,倒在其次。

—————————————————————————————————
宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
—————————————————————————————————
lxpbuaa 2003-08-21
  • 打赏
  • 举报
回复
这么作吧,将线程类再封装一下:
TReceiveThread = class(TThread)
private
FFunc: TFunc;
KernelObj: TObject;
KernelMethodName: ShortString;
protected
procedure SynKernel;
procedure Execute; override;
public
constructor Create(CreateSuspended: Boolean; KernelObj: TObject; KernelMethodName: ShortString); overload; //重载一个构造器
end;

如下实现:
constructor TReceiveThread.Create(CreateSuspended: Boolean; KernelObj: TObject; KernelMethodName: ShortString);
begin
inherited Create(CreateSuspended);
Self.KernelObj := KernelObj;
Self.KernelMethodName := KernelMethodName;
end;
procedure TReceiveThread.SynKernel;
begin
if (KernelObj <> nil) and (KernelMethodName <> '') then
with TMethod(FFunc) do
begin
Code := KernelObj.MethodAddress(KernelMethodName);
Data := KernelObj;
if Assigned(Code) then
if FFunc = 0 then ShowMessage('not assign')
else ShowMessage('assign');
end;
end;

调用:
procedure TForm1.FormCreate(Sender: TObject);
begin
Kernel := TKernel.Create;
Re := TReceiveThread.Create(False, Kernel, 'SynReceive');
end;

这次行了吧?已经完全封装。
—————————————————————————————————
宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
—————————————————————————————————
DJ_KK 2003-08-21
  • 打赏
  • 举报
回复
顶了先
XXSingle 2003-08-21
  • 打赏
  • 举报
回复
高手过招就是与众不同啊........精彩..
zjqyb 2003-08-21
  • 打赏
  • 举报
回复
//这样行不
type
TFunc = function: Integer of Object;
TReceiveThread = class(TThread)
private
FFunc: TFunc;
protected
procedure SynKernel;
procedure Execute; override;
end;

TKernel = class
private
Receive: TList;
function IsCreate: Integer;
public
constructor Create;
destructor Destroy; override;
function SynReceive: Integer;
end;

var
Kernel: TKernel;
Re: TReceiveThread;

procedure TReceiveThread.SynKernel;
begin
FFunc:= Kernel.SynReceive;
if Assigned(FFunc) then
if FFunc=0 then
ShowMessage('not assign')
else
ShowMessage('assign');
end;

procedure TReceiveThread.Execute;
begin
FreeOnTerminate := True;
Synchronize(SynKernel);
end;
function TKernel.SynReceive: Integer;
begin
Result := 0;
if Receive=nil then
Exit;
Result := 1;
end;

constructor TKernel.Create;
begin
inherited;
Receive := TList.Create;
end;

destructor TKernel.Destroy;
begin
Receive.Free;
inherited;
end;

function TKernel.IsCreate: Integer;
begin
if Receive=nil then
Result := 0
else Result := 1;
end;

bluenightsky 2003-08-21
  • 打赏
  • 举报
回复
学习
lxpbuaa 2003-08-21
  • 打赏
  • 举报
回复
copy_paste(木石三) (★★)兄:
我是从这个帖子才开始明白的,呵呵:》

—————————————————————————————————
宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
—————————————————————————————————
copy_paste 2003-08-21
  • 打赏
  • 举报
回复
嘿,不错。
我的理解有误。
虽然这方法以前用过没出错,还是没全明白其中意思。呵,多谢指点。
lxpbuaa 2003-08-21
  • 打赏
  • 举报
回复
copy_paste(木石三) (★★)兄:
不知道你发现没有,通过FFun调用GetInt2时就出错了。为什么调用GetInt1正确而GetInt2出错呢,是因为GetInt1中只是对常量1操作而GetInt2中操作了变量FInt2,取得FInt2就必须通过FFun.Data,而此时它为nil,也就是说相当于使用:
nil.FInt2
自然就错了。

—————————————————————————————————
宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
—————————————————————————————————
michaelpeng7799 2003-08-21
  • 打赏
  • 举报
回复
哦。。。。学习学习。
copy_paste 2003-08-20
  • 打赏
  • 举报
回复
var
Form1: TForm1;
Kernel: TKernel;
Re: TReceiveThread;

Form1是主窗体,与用户交互
Kernel是从谁那里引申过来,而又其它类无关时,则Kernel应为那个类的private变量,否则你定义为全局变量就是一个错误的决定。
Re如Kernel

procedure TReceiveThread.SynKernel;
begin
TMethod(FFunc).Code := TKernel.MethodAddress('SynReceive');
...
end;

FFunc是TReceiveThread的private,而SynReceive为TKernel成员函数,通赤MethodAddress来得到函数指针,如果SynReceive没有访问TKernel的成员变量(如访问Receive: TList),则好像没什么事,如果有,则会产生AV。概念不对。

function TKernel.SynReceive: Integer;
begin
Result := 0;
//线程同步类中的published方法时,Receive为inaccessible value,怎样解决?
if Receive=nil then Exit;
//同样,这句代码也不行
// if not Assigned(Receive) then Exit;
Result := 1;
end;
你没有搞清楚成员变量在一个类实例还没产生,是没有初始化的,当然会产生AV。
因为上面那句:
TMethod(FFunc).Code := TKernel.MethodAddress('SynReceive');
就不对
TMethod(FFunc).Code := Kernel.MethodAddress('SynReceive');
or
FFunc := Kernel.SynReceive;

类与类的封装很重要的一个体现就是不会使用一个莫名的全局变量,你要搞清的是每个类它的依赖关系,还有一个类实例与一个类是不一样的。
lxpbuaa 2003-08-20
  • 打赏
  • 举报
回复
amiao(射手座男孩) (▲▲▲▲):
我现在在下棋,一会回复你……

—————————————————————————————————
宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
—————————————————————————————————
amiao 2003-08-20
  • 打赏
  • 举报
回复
如果这样,还不如将SynReceive写给外面,调用也不用这么麻烦
定义TFunc为function: Integer,然后直接给FFunc赋值为SynReceive


amiao 2003-08-20
  • 打赏
  • 举报
回复
TO: lxpbuaa
这么晚了还在,PFPF...

我的意思是说不用Kernel的解决方案,因为这样封装的不好
lxpbuaa 2003-08-20
  • 打赏
  • 举报
回复
amiao(射手座男孩) (▲▲▲▲):
如果你不明白我的意思,那么直接将:
if Receive = nil then Exit;
改为:
if Kernel.Receive = nil then Exit;

或者在:
TMethod(FFunc).Code := TKernel.MethodAddress('SynReceive');
后面加一句:
TMethod(FFunc).Data := Kernel;

—————————————————————————————————
宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
—————————————————————————————————
加载更多回复(3)

5,386

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧