我之前有请教过一个关于多线程操作文件没有保护,造成程序死掉的问题,后来我去掉了一些共有变量,取消了对 ini 文件的写操作,最后暂时解决了问题.但是现在我必须要对文件的写操作,进行记录,而且后期可能需要加入的相关操作会更多.所以,多线程写文件这个问题急待解决.小弟本身也是新手,只有再次提问.查了一些相关 IdTCPServer 的资料,在代码中使用了 Synchronize 的方法,不过似乎效果不理想,运行起来会出现原来的问题:直接死掉.大概代码如下:
Threadvar
  Logip:string;......
//线程处理函数  
procedure TForm1.IdTCPServerExecute(AThread: TIdPeerThread);
var
  FStream: TFileStream;
  FileName,CmdStr,ver,ads,vstr,svr: String;
  QEnum: TQEnum;
  ini:Tinifile;
begin
  CmdStr:=AThread.Connection.ReadLn();
  Logip:=PChar(AThread.Connection.Socket.Binding.PeerIP);
  if (Length(CmdStr)=0) or (Length(CmdStr)<104) then
  begin
    Memo1.Lines.Add('非法数据来自 '+Logip);
    AThread.Connection.Disconnect;
  end;
  ver:=Copy(CmdStr,Pos('@',CmdStr)+1,32);
  //读取配置文件
  ini:=Tinifile.Create(ExtractFilePath(Application.ExeName)+'program.ini');
  vstr:=ini.ReadString(ver,'VerString','');
  svr:='['+ini.ReadString(ver,'server','')+Logip+']';
  FileName:=ini.ReadString(ver,'codefile','');
  ads:=ini.ReadString(ver,'ads','Happy gameing|');
  ini.Free;
  //获取枚举类型
  QEnum := TQEnum(GetEnumvalue(TypeInfo(TQEnum),Copy(CmdStr,Pos('<T>',CmdStr)+3,3)));
  case QEnum of
  MSG:
  begin
    AThread.Connection.WriteLn(ads);
    AThread.Connection.Disconnect;
  end;
  GET:
  begin
  if Chkconn(Logip) then
  begin
    //showmessage('1');
    Memo1.Lines.Add(svr +' 插件用户');
    //Memo1.Lines.Add(svr);
    AThread.Synchronize(Addsvr);
    try
        FStream := TFileStream.Create(ExtractFilePath(Application.ExeName)+FileName,FmOpenRead);
        AThread.Connection.WriteStream(FStream,True,False);
      finally
        FStream.Free;
        AThread.Connection.Disconnect;
    end;
  end
  else
  begin
    //showmessage('2');
    AThread.Synchronize(Delsvr);
    if Chkuser(CmdStr,svr,vstr) then
    begin
      try
        FStream := TFileStream.Create(ExtractFilePath(Application.ExeName)+FileName,FmOpenRead);
        AThread.Connection.WriteStream(FStream,True,False);
      finally
        FStream.Free;
        AThread.Connection.Disconnect;
      end;
    end
    else
    begin
      AThread.Connection.Disconnect;
    end;
  end;
  end; //end GET
  else
  begin
    AThread.Connection.WriteLn('Failed');
    AThread.Connection.Disconnect;
  end;
  end;//end case
end;......
//主要的文件操作函数
//增加IP
procedure TForm1.Addsvr;
var
  strs:Tstringlist;
begin
  strs:=Tstringlist.Create;
  strs.LoadFromFile(ExtractFilePath(Application.ExeName)+'server.ini');
  if strs.IndexOf(Logip)=-1 then
      strs.add(Logip);
  strs.SaveToFile(ExtractFilePath(Application.ExeName)+'server.ini');
  strs.Free;
end;
//删除IP
procedure TForm1.Delsvr;
var
  strs:Tstringlist;
begin
  strs:=Tstringlist.Create;
  strs.LoadFromFile(ExtractFilePath(Application.ExeName)+'server.ini');
  if strs.IndexOf(Logip)<>-1 then
      strs.Delete(strs.IndexOf(Logip));
  strs.SaveToFile(ExtractFilePath(Application.ExeName)+'server.ini');
  strs.Free;
end;

解决方案 »

  1.   

    对于可以异步处理的事情,可以交给一个独立的线程来操作.比如写文件,你可以交给一个独立的线程专职写文件,其它线程只需要交给它即可,比如使用TThreadList之类的进行中间交换.
      

  2.   

    用排他性代码保护可能出现问题的Code段。
    //全局锁
    alock:=TCriticalSection.Create;//锁定此行下面的代码,不允许其他线程访问
    alock.Acquire;
    //Some code
    //释放锁定。
    alock.Release;
      

  3.   


    我试试看,呵呵,alock 要定义为全局?应该如何定义?
      

  4.   

    我试试看,呵呵,alock 要定义为全局?应该如何定义?
    ==============
    全局变量,并且需要进行初始化.
      

  5.   

    我在现有代码中使用的 AThread.Synchronize(Delsvr); 会有问题吗,按照有关资料说这个是实现同步的啊.
      

  6.   

    这个也是可以的,只是性能稍差一点.而且需要注意Delsvr当中尽可能不操作临界区,或者注意编排好顺序,以防止死锁
      

  7.   

    我贴出来的代码应该没有使用到临界区把? 自从使用了AThread.Synchronize(Delsvr); 之后关闭时就出现,线程结束异常提示.很难关掉程序.
      

  8.   

    对了,我在线程内 有这样的代码  Memo1.lines.add 这会不会导致问题,有些文章说到在线程内部访问可视化控件也会造成冲突?
      

  9.   

    这个Synchronize还是很难用的,会引起各种奇怪的问题,比如死锁、内存访问错误。线程中直接操作TMemo是没问题的,去掉Synchronize,直接调用你的两个界面操作参数。但是受Windows缺陷影响,线程过度的刷新GUI会导致100% CPU占有率。
      

  10.   

    最后用了采用了 creatmutex 创建互斥对象的方法.现在测试中,顺便把看到的一些有用的文章贴下.谢谢大家.