注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

银河军团大本营

光荣的军团,永远的丰碑 <坚持原创>

 
 
 

日志

 
 

线程与互斥  

2009-06-03 22:37:17|  分类: 软件就是生活 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

    用了几年的C++Builder/Delphi,一般只是做应用软件的开发,这几天一直被线程问题所困绕,猛然间才发现:原来我对线程的了解是如此之少!

    事情的起因是这样的:工作中需要做一个小工具,其中有一部分是读取数据与保存数据分开的,读取数据的部分直接在主线程里做,而保存数据的部分则放在一个子线程里,这两部分通过一个互斥对象传递数据。由于保存部分的速度可能稍快些,为了让保存线程在空闲时不占系统资源,所以我在保存线程里使用了线程自身的Suspend方法来挂起线程自己,代码如下所示(以下论述均基于Delphi5,其他版本类似):

procedure TMySaveThread.Execute;
var
  oneData: TMyDataItem;
begin
  try
    while True do begin
      oneData := FMyDataQueue.Pop;
      if oneData <> nil then begin
        //do something
        oneData.Free;
      end else begin
        if Terminated then begin
          Break;
        end else begin
          Suspend;
        end;
      end;
    end;
  except
    //...
  end;
end;

    而主线程里则有以下形式的调用代码:

   mySaveThread := TMySaveThread.Create(myDataQueue);
   while not FStopped do begin
    GetDataFromServer;
    if mySaveThread.Suspended then begin
      mySaveThread.Resume;
    end;
    Application.ProcessMessages;
    Sleep(200);
  end;
  if not mySaveThread.Suspended then begin
    mySaveThread.Terminate;
    mySaveThread.WaitFor;
  end;
    这些代码在通常情况下不会出现问题,但是当主线程刚好执行到Resume,而子线程执行到Suspend时问题就出现了:子线程的Suspended变为False,而事实上它却处于挂起状态!这样,在调用WaitFor时,就会一直等待,从而使程序死掉!而这并不是每次都会发生的(请注意代码中的红色粗体字部分)。

    为了查找WaitFor有时导致程序死掉的原因,我怀疑过Delphi,我猜想是Delphi线程的WaitFor部分有Bug,为此我整整花了两天时间来做试验,并且用Google搜索了好多文章,始终找不到原因。后来自己写了个WaitFor函数,试来试去也还是不行。我都有些泄气了,打算不用WaitFor,而改用循环去判断一个标志来等待子线程执行完。然而调试时还是发生了错误:那个标志怎么也不置位,导致陷入死循环!我忽然想到,是不是线程挂起了?于是在调试窗口里执行了几下mySaveThread.Resume,哇哈!线程开始走起来,并最终在保存完myDataQueue里的数据之后结束了!一刹那间我忽然明白了:是Suspened的混乱导致线程处于假死状态,从而导致WaitFor失败,仔细研究了TThread的相关代码,我确信了这一点,请看以下TThread代码:

procedure TThread.Suspend;
begin
  FSuspended := True;
  SuspendThread(FHandle);
end;

procedure TThread.Resume;
begin
  if ResumeThread(FHandle) = 1 then FSuspended := False;
end;
    因为FSuspended的读写并未进行互斥操作,导致主线程执行Resume,同时子线程执行Suspend时可能出现以下情况:

1.主线程执行了ResumeThread(FHandle)

2.子线程执行了FSuspended := True

3.主线程执行了FSuspended := False

4.子线程执行了SuspendThrad(FHandle)

    在子线程执行完Suspend之后,本来Suspended也应该是True,但却被置成了False,于是后面再执行WaitFor时就是在等待一个事实上已经挂起的线程结束,也就陷入了死循环,导致程序假死!

    最终的结论是:不要在线程里调用自身的Suspend/Resume等方法,由于Suspended变量不是一个互斥对象,在线程里调用自身的Suspend/Resume方法极有可能导致线程假死!

    最后,我改在主线程里调用子线程的Suspend方法,子线程里调用Suspend的地方改成了Sleep,于是问题也就随之解决了!

    看来,对于线程,我还需要深入了解啊。

  评论这张
 
阅读(490)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018