DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: sheik8888
今日帖子: 35
在线用户: 28
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/8/30 15:34:00
标题:
一个困扰多年的软件问题。。。关于多线程时不时access violation和fastmm泄漏 浏览:1220
加入我的收藏
楼主: 实在是困扰多年,软件一直将就用,很痛苦,在此希望得到各位的帮助。
简单背景:
主界面一些表格,开了一个线程对一个数据文件进行操作,创建内存映射文件来读和写。每次读写都做为一个item, 这个item在整个流程中会执行很多次,由客户来设置在哪里执行,每个地方运行到了就会执行一次。
现在的问题是: 
单击这个功能正常,循环100,1000,10000次也正常。 一旦用户使用,因为还有别的item杂在里面,运行久了,有时几分钟,有时几个小时,会在某个item的地方(这个地方每次都不一样,无法控制)
1: 弹出access vialition,程序停止。  
2:或者是弹出fastmm 一堆的乱码。

鉴于以上的情况,断点没法弄,只能查看fastmm的泄露报告,看也看不懂,一堆的乱码。

求教各位有经验人士,怎么来查这个问题?

我搜索了论坛,看到
http://bbs.2ccc.com/topic.asp?topicid=406290
查出来两个ide的bug,这两个bug也是时不时AV。完全不知道如何操作的。
网上也查了好久,baidu google研究了很久,实在是太菜了。
我用的是c++builder6。
网上对于查看fastmm的报告来断点也没找到个靠谱的,而且这个错误实在是没有办法测试,执行一次有时要好几个小时才随机在某个item弹出。人要疯了。。。

在此请教各位 “方法” “关键字”  “想法”。。。。
----------------------------------------------
-
作者:
女 hecongzhen (令狐**) ▲▲▲▲▲ -
普通会员
2018/8/30 15:51:14
1楼: 通常这种问题是多线程同步造成的。。

建议:既然水平次,就别用多线程了,改成单线程
----------------------------------------------
-
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2018/8/30 16:33:38
2楼: item是什么?界面item还是excel的item?如果是界面的,你只用自带的就好。

另外内存使用以后不释放,也会造成一段时间后的崩溃,崩溃地点是任意的,很具有迷惑性,想破头也想不出来为什么,实际你的代码逻辑没有问题,只是内存没释放而已。
----------------------------------------------
只有偏执狂才能生存!
作者:
男 peng_delphi (我去) ▲▲▲▲▲ -
普通会员
2018/8/30 16:33:46
2楼: 1、多线程方面,不晓得你是不是没有临界资源互斥?
2、建议你在读写共享内存时,判断一下是否为空或是否存在
3、建议你做异常处理try. . . except,抛出异常信息。
----------------------------------------------
程序在于业务、流程、思想、风格。
作者:
男 wr960204 (武稀松) ★☆☆☆☆ -
盒子活跃会员
2018/8/30 18:55:47
3楼: 有几处疑问,
1.你的线程访问UI控件啦?
2.你的线程如果是调用CreateThread创建的,但没有设置IsMultiThread标志?
3.线程访问对象或者内存没加互斥?
你查一下,多数应该落在这三处之内
----------------------------------------------
武稀松http://www.raysoftware.cn
作者:
男 xuchuantao (暗黑天使) ▲▲▲▲△ -
注册会员
2018/8/30 19:25:07
4楼: 你需要写日志,崩溃后查看日志内容看看程序干了什么.再分析问题出在那里.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/8/30 20:12:21
5楼: 感谢楼上各位的提示。。。 
1.水平的确不行,但是不能不用,业务要求没办法。
2. item就是客户可以设置的一个地方,客户加一个item,那么程序就执行某个线程中的函数一次。
  
1. 线程没有访问控件,都是发个消息给主界面,主界面截取msg。然后也加了同步。
2. IsMultiThread我搜索了一下,还真没有。。。我靠。那为啥用手工执行个几十遍都没事,一旦自动起来流水下去就弹。这个我来改一下。



这个是创建线程,搜索了整个项目没有Is...
        m_ThreadProcess = new TProcessThread(true , this->Handle , &m_vstProcessList );     //注意标量的生存期 用全局变量或者类的成员变量传递参数
        if( m_ThreadProcess == NULL )
        {
          m_vstProcessList.clear();
          return false;
        }
3.线程访问对象或者内存没加互斥?
我来试试到处加上。。。。
4. 写日志。。这个感觉无从下手,因为出问题的地方和时间感觉都随机的。。。
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/8/30 20:36:44
6楼: 针对武大侠的IsMultiThread问题,我最先来测试一下。。。可能需要一段时间。

主线程我感觉是加这里,pvstPipeList这些事我传递的一些东西。
__fastcall TFltThread::TFltThread(bool CreateSuspended, _stPipeTransPara* pstPipeTransPara,  vector< _stPipeList > *pvstPipeList )
          : TThread(CreateSuspended)
{
   //其他代码

    IsMultiThread = true; //<-----感觉是加这里,不知道对不对。
}


我的每个item都是用的dll。主exe创建线程,执行dll的函数。
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
    dllHinst = hinst;

    if( reason == DLL_THREAD_ATTACH )
    {
        IsMultiThread = true; //我每个dll都加这里,不知道对不。
    }

    return 1;
}


网上查的,看不太懂delphi。。。照抄的。
library xxx;
var
OldDllProc: TDLLProc;
procedure ThisDllProc(Reason: Integer);
begin
if Reason = DLL_THREAD_ATTACH then
    IsMultiThread := True;
if Assigned(OldDllProc) then
    OldDllProc(Reason);
end;
begin
OldDllProc := DllProc;
DllProc := ThisDllProc;
ThisDllProc(DLL_PROCESS_ATTACH);


请各位帮忙指正
----------------------------------------------
-
作者:
男 bahamut8348 (leonna) ▲▲▲▲▲ -
普通会员
2018/8/30 23:25:48
7楼: 其实吧,你都涉及到文件读写了,这样多线程没什么用了。io操作总是串行的。

另外就是,说到同步,并不是多线程就必须同步的,只要你不涉及到公共资源,或者你的公共资源可以合理调度,根本不需要加锁,很典型的就是无锁队列。

但是,子线程里不能有ui操作,如果必须ui响应的话,win下可以通过消息去同步。
----------------------------------------------
--
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/8/31 8:12:44
8楼: 楼上的。    每个item代表不同的功能, 整个流程很多item设置下来,时间非常长,所以整个流程全放入线程里面执行,主页面只负责跑个进度条之类。
----------------------------------------------
-
作者:
男 hexi (Hexi) ★☆☆☆☆ -
盒子活跃会员
2018/8/31 8:19:47
9楼: 多个线程可能同时访问的资源,需要加锁。
uses System.SysObjs;
...

var  
 Locker:TCriticalSection;


...所有访问资源的地方都应该这样:
  Locker.Enter;
  try
    访问资源;
  finally
    Locker.Leave;
  end;
...

如果是更新界面要用TThread.Synchronize,如果是DLL中线程要更新界面,
需要做些处理,请看:
https://www.cnblogs.com/hezihang/p/3965179.html
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/8/31 9:17:30
10楼: ls 的 TThread.Synchronze(Proc)  好文。
我没敢再dll中操作控件,所有要操作的都是发个消息到主页面,主程序截获后处理,目前没发生过问题。

错误1: 现在弹出的窗口提示是 fastmm has detectd...during a FreeMem/Getmm operation.弹出很大一个窗口,确认后程序居然还能继续往下走    多出现在我的一个拷贝的动作发生 ,把某个文件从一个地方拷贝到另外一个地方,我来好好检查下。。。。
2: 弹出AV错误,直接就停止了。这个多出现在一个对内存映射文件操作的地方。但是这个功能我用前台循环个几万次都没事。。。。

再继续求助各位给点提示和方法,思想之类。
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/8/31 9:19:33
11楼: 昨天加上了IsmultiThread。exe和dll都按6楼的加了。没啥变化差不多。我也纳闷没加居然时不时还运行得挺好。。。   可能我的线程只开了一个,运行时主界面只走进度条,本质等效一个不会界面卡死的单线程程序。。。
----------------------------------------------
-
作者:
男 draculamx (draculamx) ▲▲△△△ -
注册会员
2018/8/31 10:30:12
12楼: 楼主还是放些代码出来看看,前面讨论了这么多,大家都在猜,效率太低了,如果你想快点解决问题的话。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
男 hdcopy (hdcopy) ▲▲▲▲▲ -
普通会员
2018/8/31 14:25:22
13楼: 看不到代码,也不想看代码,几年了,楼主没时间解决吗?
“开了一个线程对一个数据文件进行操作,创建内存映射文件来读和写。每次读写都做为一个item, ”,假定楼主说的都是真的,线程中没有UI操作。
那数据文件是不是就是文件,有没有读写冲突的可能。。屏蔽后续操作,测试。
创建内存映射文件是什么鬼,流?内存表?自定义结构数组?创建,释放的代码,
屏蔽其他操作,测试。
读写and其他。。。。,分开分开,单独测试。
----------------------------------------------
-
作者:
男 bahamut8348 (leonna) ▲▲▲▲▲ -
普通会员
2018/8/31 16:24:36
14楼: 很慢??看兰州描述了这么多东西。
然而,有考虑线程总数、内存总数么?

比如你一个4G的文件,你不会直接全部端进内存吧?

其实很不理解你这种结构。
----------------------------------------------
--
作者:
男 hz_2009 (盒子) ▲▲▲▲△ -
注册会员
2018/8/31 18:06:24
15楼: 内存溢出。。
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/8/31 20:28:44
16楼: 多谢ls各位了。。。  代码太多了。。。我都不知道该拷贝哪一段。   单次执行又没有问题。    文件最大也就几十M,一次性读入的。。。

关键是单次执行都没问题。 。。。
----------------------------------------------
-
作者:
男 bahamut8348 (leonna) ▲▲▲▲▲ -
普通会员
2018/9/1 0:58:53
17楼: 自己算一下线程总数、文件总大小、内存总大小。。。

看看是不是线程超2000了?是不是申请的内存超过1G以上了?是不是内核句柄超标了?
----------------------------------------------
--
作者:
男 kuei (kuei) ★☆☆☆☆ -
盒子活跃会员
2018/9/1 6:37:40
18楼: 提供写Thread的经验,提供参考,
如下Code,在Execute中,除了逻辑判断以外,
所有操作(数据库读写、文件读写....等)全都用事件导到外部处理,
这样就不会影响Thread正常运行,也不会发生Lock问题。

procedure TDogThread.Execute;
begin
  while not Terminated do
  try
    if Assigned(OnJob_Get) then
    begin
        try 
          OnJob_Get(Now, 2, 0);
        except
          on E: Exception do
          begin
          ShowMsg('OnJob_Get, Error :' + E.Message);          
          end;
        end;
    end;

    SleepEx(50, False);   
  except
  end;
end;
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/9/1 11:13:15
19楼: ls的 你这个思路可能对我这个有针对性。

其中一个item经常出问题。其实就是一个很简单的copy文件。所以我实在没招了。

我把它postmessage出去 让主线程来搞 试试。。。
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/9/1 11:14:17
20楼: 9楼的。 我看了下我的dll经常同步,但是还好,目前没有发现死锁的问题。。我先不管这一个
----------------------------------------------
-
作者:
男 mike1234567890 (Mike) ▲▲▲▲▲▲ -
普通会员
2018/9/1 13:26:34
21楼: 我前一个月刚调了一个多线程的东西,把QPlugins和ZServer4D集成到一起,用我自己写的系统服务来跑,经常会卡住退不出来,后来确定是线程调度问题,在等待期间让处理线程获取CPU就行了,你可以参考一下
----------------------------------------------
-
作者:
男 kylix2008 (kylix2008) ★☆☆☆☆ -
普通会员
2018/9/2 8:51:24
22楼: 这个讨论很有价值,收藏了。
多线程操作,功底不好的话,建议楼主试试QDAC(不知道是否支持c++builder6),功能很强大的,支持作业队列。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ▲▲▲▲▲ -
普通会员
2018/9/2 17:00:25
23楼: 多线程,如果你明白了它的工作原理,其实蛮简单的。遵循几个简单的规则,就保证不会出错了。

至于 access vialition 错误,通常是操作了不存在的指针或者对象。当然这种情况也可能比较复杂,比如基于接口引用计数的对象因为接口引用释放而对象被释放了,另外有个地方却在使用它的对象实例,也属于操作了不存在的对象。又或者,多个线程同时操作了同一个内存(相同的内存也可能是一个相同的对象实例)。

另外,内存泄露,如果你是完全基于对象的代码,看看哪些地方该释放对象而忘记了释放。如果代码架构太复杂,写代码无法做到哪里创建了对象就在哪里释放,就采用基于接口引用计数的对象,只使用接口,不使用对象实例。则那个接口没人用的时候,对象会自动被释放掉,也就没有内存泄露了。
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/9/2 20:34:44
24楼: 我觉得还是我太菜了。。。
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/9/3 9:32:53
25楼: 多谢ls各位花费时间来看帖回帖,经过再三思考和各种情况的评估,现在决定用berlin重新开发。
之前的架构是 主程序 +  很多dll,每个dll都是一个功能,比如
dll1: 删除某文件
dll2: 拷贝文件A到B
dll3: 打开文件c,根据里面的设置进行一些运算。

现在准备不用这种,全部在一个exe里面实现。。。。

之前dll1,2,3 等式先用vector在主线程循环读入设置,然后开一个线程,1,2,3所有的功能全部在一个大循环内switch来运行。

现在请教各位有经验的是。
从dll1...10000是各种不同或者相同的dll,我是每个开线程+执行+关线程,还是开线程+ 执行1...1000 + 关线程比较好?

不管时间,稳定最重要,,,从一开始执行到底,不能再有奇葩的getmm freemm rellocmm 跳出来打断了。。。。。
----------------------------------------------
-
作者:
男 hdcopy (hdcopy) ▲▲▲▲▲ -
普通会员
2018/9/3 17:12:54
26楼: dll相当于子函数,线程为了不锁界面?
如果只运行1个线程来搞,没问题。
如果是运行多个线程,看不出会有什么改进。。。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ▲▲▲▲▲ -
普通会员
2018/9/4 22:45:32
27楼: 楼上的,多线程和 DLL 没关系啊。DLL 也是代码,只不过不是源代码。

如果写多线程,你一定要知道你现在写的这个函数,实际跑起来的时候,是哪个线程在执行它。

另外,如果你搞了 getmm freemm 等等,代码一定要严谨,否则内存肯定会出错。如果你做不到,就不要搞这种自己管理内存的操作,全部改为面向对象,而且对象也不要自己创建,用基于接口的对象。这样如果接口引用没有了,它自动释放,就不会有内存泄露问题。

我老人家03年写的东西里面就几十个线程同时跑,也从来不出事。现在随便写一个东西都有一堆线程在跑。

另外,现在发现 DELPHI 的异步,TTask.Run 这个很好用。其实它就是线程,但可以直接把一段代码包起来,不用单独写一个 TThread 的继承,代码简洁多啦。
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/9/5 21:03:18
28楼: 这么多年了。这次发狠终于查到bug了。很奇怪的一个bug。
主页面的一个progressbar用于线程的进度指示,去掉就再不随机弹了。我大致已经
分析出内部的机理,正在测试中,等搞定了来告诉大家这个奇葩的问题。
----------------------------------------------
-
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2018/9/5 21:53:05
29楼: 不用测试,就是你自己的问题。要么是除零错误,要么是消息同步到主界面的错误,要么是空指针。
----------------------------------------------
只有偏执狂才能生存!
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/9/5 22:01:52
30楼: ls的。很可能是 devexpress vcl里面的cxgrid 有一列unbound数据用做进度条的冲突。

我在子线程里 发消息让主界面cxgrid
1: 进度条加到100%
2: 切换到下一行,并选中。

很可能这两个切换冲突了。我准备加个锁。   

目前偶尔会弹出一样的错误,但是少很多了。
所以我继续试验。
----------------------------------------------
-
作者:
男 lordaeron (Terry) ▲▲▲▲▲▲ -
注册会员
2018/9/6 13:48:29
31楼: 使用multhithread 在GUI 中有鐵律
1. 所有跟USER 有關的交互操作,都必需通過MAIN THREAD
2. 所有sub thread的結果,都必需通過MAIN THREAD 呈現給USER
3. thread data exchange 必需lock起來,像使用string又不LOCK 的話,access violation 就會跟著你。
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/9/7 20:43:21
32楼: ls的  :
    SendNotifyMessage( m_stParaOwner.handle,  WM_MY_COMPOENT_CTRL, SC_THREAD_START ,  0 );
    Application->ProcessMessages();

所有的数据子线程都是发消息出去。  主界面处理消息。。。。

错误时不时出现,有时不出现。。。太痛苦了。
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/9/9 21:05:27
33楼: 各位同学。。。 我改了一个位置。。现在好多了。还有一点点问题。我新开帖子了。
SendNotifyMessage( m_stParaOwner.handle,  WM_MY_COMPOENT_CTRL, SC_THREAD_START ,  0 );
    Application->ProcessMessages();

我在子线程(只有一个)里面如上代码。。。。我去掉了Application->ProcessMessages(); 错误几率大幅度减少。。。。以前是10次循环要弹出个5,6次,现在是50次弹出个1,2次。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ▲▲▲▲▲ -
普通会员
2018/9/13 10:20:44
34楼: 楼主,我发现你可能用线程操作了界面UI。

不管你的线程调用了什么东东,如果那个东东最终导致了 UI 的变化,就一定要做一个同步。所谓同步,其实就是线程把那个东东的调用交给主线程,等主线程跑完UI变动后,线程再继续往下执行。比如你有个函数会导致界面UI变动:

procedure showMyCount(const i: Integer);
begin
  LabelMyCount.Caption := i.ToString;
end;

比如你有一段代码,是在线程里面执行:

for i := 0 to 99999 do
begin
  Do some thing...这里非常耗时比如去访问某个巨慢的网站,所以这个 for 循环才需要放到线程底下去跑,如果用主线程跑,则会让界面冻结半天没响应。
  ShowMyCount(i); //这里显示是第几次做上面的 Do some thing 操作。
end;

上面的代码,加上把这一段 for 循环放到某个线程底下去跑,则执行 ShowMyCount 的时候,可能会出问题。因为调用这个 for 循环的线程,操作了界面 UI。

那怎么办呢?很简单,加上同步,变成:

TThread.Synchronize(nil,
    procedure
    begin
      ShowMyCount(i);
    end
  );

----------
在 Delphi 里面,之前我们要把一段代码放到线程里面去跑,代码写好了,封装成函数了,或者封装成一个类的方法了,就需要自己写一个线程类,类似:

TMyThread = class(TThread)

然后在这个自己写的线程类里面的 Execute 方法里面去调用那段代码。这样就需要多写很多代码。

现在的 Delphi 加上了一个所谓的异步方法,其实也是线程,但代码就简单了,这样的东西,只是代码上简单了,背后的本质没变,所以叫:语法糖。

因此,上面的 for 循环,我们可以这样来写:

TTask.Run(
  procedure
  begin
     for i := 0 to 99999 do
     begin
       Do some thing.....//这句话很费时间,所以把它放到线程里。
       
       TThread.Synchronize(nil,
         procedure
         begin
          ShowMyCount(i);  //这句话会影响界面,而这里又是线程调用它,所以要同步一下。
         end
        );
     end;
  end

);


----------

再次重复:所谓的同步,也就是把一个方法放进 TThread.Synchronize() 里面去跑,就是调用这个 TThread.Synchronize 的线程,在这里阻塞(停下来),等待主线程把里面的方法跑完,然后该线程再继续向下执行。在这里,就是 ShowMyCount 这个方法是由主线程去执行,也就是影响界面UI的事,丢给主线程去干。

再重复一遍:线程直接去调用影响界面UI的代码,会出问题 -- 有时候也不会出问题,看你的运气。
----------------------------------------------
-
作者:
男 roguebear (旺财) ▲▲▲▲△ -
注册会员
2018/9/14 21:25:14
35楼: 已经搞定。我开了新帖说明
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v2.1 版权所有 页面执行46.875毫秒 RSS