DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: huoying_you
今日帖子: 10
在线用户: 13
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 litou (泥头) ★☆☆☆☆ -
盒子活跃会员
2012/10/18 17:44:15
标题:
多线程用消息传递类对象的问题 浏览:1738
加入我的收藏
楼主: 一程序,有主窗体和一工作线程,现两者间通过消息传递内容并执行,工作线程的消息循环已经建立,主窗体postthreadmessage到工作线程,工作线程postmessage到主窗体,由于相互传输的数据根据功能不同而不同,故不能统一传输格式,只能根据实际的数据创建不同的类(dto)来传输,举个例子
主窗体登陆:在登陆事件中创建登陆数据类局部变量TLoginData,并用postthreadmessage发送消息,消息中wparam是Integer(@LoginData)
工作线程接收时还原类PLoginData(Msg.wparam)^便取得相应的数据,使用后在工作线程free掉
实际过程中发现在线程中有时获取成功有时不行,并且报内存错误,难道是创建类示例的是局部变量,退出登陆函数后局部变量被系统自动回收?尽管变量被回收,但指针指向的数据没有被free掉,在线程中应该也可以获取吧?

用sendmessage肯定没问题,但程序需要只能用postmessage,否则线程处于等待状态。
----------------------------------------------
-
作者:
男 leozem (leozem) ★☆☆☆☆ -
盒子活跃会员
2012/10/18 18:06:13
1楼: TLoginData用局部变量,有可能在线程中还没接收完就被释放了。
TLoginData定义成指针,PLoginData = ^TLoginData;
var
  lpLoginData : PLoginData;
begin
  New(PLoginData);
  PLoginData^.......
  ........
  赋值操作
  postmessage。。  wparam是Integer(lpLoginData )
end;

接收:
var
  lpLoginData : PLoginData;
begin
  lpLoginData := PLoginData(Msg.wparam);
  操作

  最后释放掉
  Dispose(lpLoginData);
end;


更好的方法楼下补充。。。
----------------------------------------------
-
作者:
男 litou (泥头) ★☆☆☆☆ -
盒子活跃会员
2012/10/18 19:49:27
2楼: 感谢leozem,其实你表达的就是我的意思,只是类我是用构造函数Create,而不是new出来的。类能够用new吗?我搜索过网上说消息传递消息都是用record,record用new就可以,但类也可以么?
我跟leozem的区别是,leozem是new指针出来,使用后dispose,我的是create一个类,最后free掉,我先尝试一下new是否成功,感觉跟create是一个道理啊??

我还是把我的重点代码贴一下出来直观一些,讨论也方便
主窗体向线程发送消息可以收到,但线程向主窗体发送消息时就报内存错误了。
主窗体登陆发送:
procedure TfrmMain.btnLoginClick(Sender: TObject);
var
  LD: TLoginData;
begin
  LD := TLoginData.Create;
  。。。
  PostThreadMessage(FThread.ThreadID, WM_CUSTOM, Integer(@LD), 0);
end;

线程接收登陆请求:
procedure TCustomThread.Execute;
var
  Msg: TMsg;
  LD: TLoginData;
begin
  while not Terminated and GetMessage(Msg, 0, 0, 0) do
  begin
    if Msg.message = WM_CUSOM then
    begin
      LD := PLoginData(Msg.WParam)^;
      if Assigned(LD) then
      begin
        try
          。。。
        finally
          LD.Free;
        end;
      end;
    end;
  end;
end;

反过来线程发送登陆结果:
procedure TCustomThread.Login;
var
  LDR: TLoginDataResult;
begin
  LDR := TLoginDataResult.Create;
  。。。
  PostMessage(MainForm.Handle, WM_CUSTOM, Integer(@LDR), 0);
end;

来窗体接收登陆结果
procedure ReceiveWorkThreadMessage(var Msg: TMessage); message WM_CUSTOM;

procedure TfrmMain.ReceiveWorkThreadMessage(var Msg: TMessage);
var
  LDR: TLoginDataResult;
begin
  LDR := PLoginDataResult(Msg.WParam)^;
  if Assigned(LDR) then
  begin
    try
      。。。
    finally
      LDR.Free;
    end;
  end;
end;
----------------------------------------------
-
作者:
男 leozem (leozem) ★☆☆☆☆ -
盒子活跃会员
2012/10/18 20:11:25
3楼: 我以为你TLoginData是一个Record,所以采用指针New的方式,如果你的TLoginData是一个class,就必须要用你的方法Create和free。New只是申请一段指针空间,而Create不仅仅申请还有一些类的初始化。

你提到的问题:主窗体向线程发送消息可以收到,但线程向主窗体发送消息时就报内存错误了。
初略的看了下代码,写法没什么问题。
你的MainForm是一个全局变量还是?

反过来线程发送登陆结果:
procedure TCustomThread.Login;
var
  LDR: TLoginDataResult;
begin
  LDR := TLoginDataResult.Create;
  。。。
  PostMessage(MainForm.Handle, WM_CUSTOM, Integer(@LDR), 0);
  //调试下这里面的MainForm看看是不是空指针。
end;
----------------------------------------------
-
作者:
男 litou (泥头) ★☆☆☆☆ -
盒子活跃会员
2012/10/18 20:25:18
4楼: to leozem,MainForm.Handle其实就是主窗口的句柄,我在Create这个线程的时候已经把Handle传进来了,我这样写是让大家看得明白这是主窗口的句柄。

出错的地方是这里:
来窗体接收登陆结果
procedure TfrmMain.ReceiveWorkThreadMessage(var Msg: TMessage);
var
  LDR: TLoginDataResult;
begin
  LDR := PLoginDataResult(Msg.WParam)^;
  if Assigned(LDR) then
  begin
    try
      LDR.xxx = xxx; //这里出错
    finally
      LDR.Free;
    end;
  end;
end;

经过调试和watch,线程发的那个Integer(@LDR)与接收的Msg.WParam值是一致的,排除值接收错误的原因,当接收处理到LDR := PLoginDataResult(Msg.WParam)^;这部时,调试显示LDR为inaccessible value,虽然程序能继续走下去,但到下面对LDR取值时就出现内存出错,可以肯定转换出来的LDR不是有效的LDR类型,但是WParam值又是对的,估计是WParam指向的对象被系统回收了,但是前面又没有free,所以觉得很奇怪。我曾经担心过这个指针值是因为没有被使用而被系统回收(类似Java、C#的回收机制)而使用了TList存放LD和LDR,但结果依然。我是使用FastMM的,退出时也没有报内存泄露,真奇怪。
----------------------------------------------
-
作者:
男 leozem (leozem) ★☆☆☆☆ -
盒子活跃会员
2012/10/18 20:46:16
5楼: 没释放不会被系统回收。
奇怪了,代码没问题。
1、32位系统?
2、线程是否在Dll中?
----------------------------------------------
-
作者:
男 litou (泥头) ★☆☆☆☆ -
盒子活跃会员
2012/10/18 20:48:43
6楼: to leozem,32位xp,d7,纯win32程序无任何dll,我再继续调试排除一下,既然代码无错可能是一些小地方引起的,感谢leozem
----------------------------------------------
-
作者:
男 litou (泥头) ★☆☆☆☆ -
盒子活跃会员
2012/10/18 21:06:54
7楼: 从主窗体发送至线程,运行时能够得到转换成功的LD,但调试时得到的LD竟然是nil,再调试LD就编程inaccessible value了,难道真的不能够用类指针传递?因为网上搜到的都是用record,但record存放字符串会有问题吧?
----------------------------------------------
-
作者:
男 hexpate (蜗牛大仙) ▲▲▲▲▲ -
普通会员
2012/10/18 21:19:30
8楼: 程序你显然写错了, 传递实例指针, 不是传局部变量指针, 所以你每次postthreadmessage 传递的指针都是错的, 根本不是实例的地址, 你应该这么写Integer(LDR) 而不是 Integer(@LDR), 另外接受到消息后, 直接转换回来即可不需要  PLoginDataResult, PLoginData这么2个东西, 按下面方式改
LDR := TLoginDataResult(Msg.WParam);
LD := TLoginData(Msg.WParam);
----------------------------------------------
一只蜗牛
作者:
男 hexpate (蜗牛大仙) ▲▲▲▲▲ -
普通会员
2012/10/18 21:21:28
9楼: 2处PostThreadMessage 和2处消息处理的地方都要改哦. 别漏了 :)
----------------------------------------------
一只蜗牛
作者:
男 litou (泥头) ★☆☆☆☆ -
盒子活跃会员
2012/10/18 21:42:22
10楼: 经测试,果然如hexpate所言,应该传实例指针,这是我受网上传record参数的方法影响,没有注意到类的实例与record在指针上的本质不同,再次感谢hexpate指出错误,也感谢leozem的帮助,该问题已完满解决。
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行2011.719毫秒 RSS