对数据库的多线程操作出现死锁,如何处理?(200分)(200分)

  • 主题发起人 主题发起人 yuking_cc
  • 开始时间 开始时间
Y

yuking_cc

Unregistered / Unconfirmed
GUEST, unregistred user!
说明:
动态创建的线程中包含带有事务处理的数据库操作,当开多个线程是,总是不固定地出现
进程被死锁并被牺牲的情况,如何处理?
程序代码:
procedure TSnatchThread.SaveRawSource(sBody: string);//保存网页源代码
var
adosp: TADOStoredProc;
adoq, adoq2: TADOQuery;
sSql, sSql2: string;
sPageID, sPageAddr: string;
begin
adoq := TADOQuery.Create(nil);
adoq.Connection := adocn2;
adoq2 := TADOQuery.Create(nil);
adoq2.Connection := adocn2;
adosp := TADOStoredProc.Create(nil);
adosp.Connection := adocn2;
adosp.ProcedureName := 'InsertTextRaw';
with adosp.Parameters.AddParameterdo
begin
Name := '@id_value';
DataType := ftInteger;
Direction := pdInput;
end;
//with
with adosp.Parameters.AddParameterdo
begin
Name := '@insert_cont';
DataType := ftWideString;
Direction := pdInput;
Size := 1073741823;
end;
//with
with adosp.Parameters.AddParameterdo
begin
Name := '@web_id';
DataType := ftInteger;
Direction := pdInput;
end;
//with
//--------------------------------------------------------------------------//
sPageAddr := StringReplace(s_Addr, '''', '''''', [rfReplaceAll]);
//插入新地址
adoq.Close;
adoq.SQL.Clear;
sSql := 'insert into ws_page_addr_raw(ws_id,ws_page_addr) values('+s_WebID+','''+sPageAddr+''')';
adoq.SQL.Add(sSql);
//产生新文章ID号
adoq2.Close;
adoq2.SQL.Clear;
sSql2 := 'select top 1 arti_id from information where arti_web = '+s_WebID+' order by arti_id desc';
adoq2.SQL.Add(sSql2);
sPageID := '1';
try
adoq2.Open;
if adoq2.RecordCount > 0 then
sPageID := IntToStr(adoq2.FieldByName('arti_id').AsInteger + 1);
except
on E: Exceptiondo
begin
WriteLog('['+s_WebID+']Select:'+E.Message, 'Error_SaveRawSource.txt');
end;
//on
end;
//try
//插入文章基本信息
adoq2.Close;
adoq2.SQL.Clear;
sSql2 := 'insert into information(arti_web,arti_id,i_address,i_content_raw,i_done) values('+s_WebID+','+sPageID+','''+sPageAddr+''',''LXQ'',0)';
adoq2.SQL.Add(sSql2);
//插入原文
adosp.Parameters.ParamByName('@id_value').Value := StrToInt(sPageID);
adosp.Parameters.ParamByName('@insert_cont').Value := sBody;
adosp.Parameters.ParamByName('@web_id').Value := StrToInt(s_WebID);
adocn2.begin
Trans;//开始事务
try
adoq.ExecSQL;
adoq2.ExecSQL;
adosp.ExecProc;
adocn2.CommitTrans;//提交事务
except
on E: Exceptiondo
begin
adocn2.RollbackTrans;//回滚事务
WriteLog('['+s_WebID+']Trans:'+E.Message, 'Error_SaveRawSource.txt');
end;
//on
end;
//--------------------------------------------------------------------------//
adoq.Close;
adoq.Free;
adoq2.Close;
adoq2.Free;
adosp.Free;
end;

错误提示:
[程序号:1][2002-8-2 11:25:11]:Select:事务(进程 ID 109)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:11]:Select:事务(进程 ID 83)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:12]:[1077]Trans:事务(进程 ID 85)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:12]:[1077]Trans:事务(进程 ID 91)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:12]:[1077]Trans:事务(进程 ID 79)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:12]:[1077]Trans:事务(进程 ID 81)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:12]:[1077]Trans:事务(进程 ID 121)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:14]:Select:事务(进程 ID 133)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:14]:[1077]Trans:事务(进程 ID 103)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:14]:Select:事务(进程 ID 123)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:14]:[1077]Trans:事务(进程 ID 101)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:14]:Select:事务(进程 ID 137)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:14]:[1077]Trans:事务(进程 ID 99)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:14]:[1077]Trans:事务(进程 ID 97)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:14]:[1077]Trans:事务(进程 ID 83)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:15]:[1077]Trans:事务(进程 ID 137)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:15]:Select:事务(进程 ID 151)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:15]:[1077]Trans:事务(进程 ID 123)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:15]:Select:事务(进程 ID 139)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:15]:[1077]Trans:事务(进程 ID 133)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:15]:[1077]Trans:事务(进程 ID 131)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
[程序号:1][2002-8-2 11:25:15]:Select:事务(进程 ID 147)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
 
多个线程发生死锁?
你需要用互斥来同步线程!晚些时候我给你部分关键代码:)
 
1、你先要在创建任何线程前定义并创建一个全局的互斥对象:
var
hMutex:THandle;
在程序的初始化部分创建该对象(在FormCreate或Initialization里都可以)
hMutex:=CreateMutex(nil,false,nil);
2、然后在你的线程体中将你需要保护的地方使用如下代码:
if WaitForSingleObject(hMutex,INFINITE)=WAIT_OBJECT_0 then
begin
//在此处添加你需要对数据库进行操作的代码
end;
//释放互斥对象,使其为发信号状态
ReleaseMutex(hMutex);
3、注意,别忘了在你的应用程序终止前用CloseHandle(hMutex)来删除该互斥对象,不然它将
在系统中一直存在(它是系统全局的对象);
所以,你可以在线程的Execute中使用上面的代码,也可以在你的线程函数中使用,就你上面的
代码,可改动为如下代码:
procedure TSnatchThread.SaveRawSource(sBody: string);//保存网页源代码
var
adosp: TADOStoredProc;
adoq, adoq2: TADOQuery;
sSql, sSql2: string;
sPageID, sPageAddr: string;
begin
if WaitForSingleObject(hMutex,INFINITE)=WAIT_OBJECT_0 then
begin
adoq := TADOQuery.Create(nil);
adoq.Connection := adocn2;
adoq2 := TADOQuery.Create(nil);
adoq2.Connection := adocn2;
adosp := TADOStoredProc.Create(nil);
adosp.Connection := adocn2;
adosp.ProcedureName := 'InsertTextRaw';
with adosp.Parameters.AddParameterdo
begin
Name := '@id_value';
DataType := ftInteger;
Direction := pdInput;
end;
//with
with adosp.Parameters.AddParameterdo
begin
Name := '@insert_cont';
DataType := ftWideString;
Direction := pdInput;
Size := 1073741823;
end;
//with
with adosp.Parameters.AddParameterdo
begin
Name := '@web_id';
DataType := ftInteger;
Direction := pdInput;
end;
//with
//--------------------------------------------------------------------------//
sPageAddr := StringReplace(s_Addr, '''', '''''', [rfReplaceAll]);
//插入新地址
adoq.Close;
adoq.SQL.Clear;
sSql := 'insert into ws_page_addr_raw(ws_id,ws_page_addr) values('+s_WebID+','''+sPageAddr+''')';
adoq.SQL.Add(sSql);
//产生新文章ID号
adoq2.Close;
adoq2.SQL.Clear;
sSql2 := 'select top 1 arti_id from information where arti_web = '+s_WebID+' order by arti_id desc';
adoq2.SQL.Add(sSql2);
sPageID := '1';
try
adoq2.Open;
if adoq2.RecordCount > 0 then
sPageID := IntToStr(adoq2.FieldByName('arti_id').AsInteger + 1);
except
on E: Exceptiondo
begin
WriteLog('['+s_WebID+']Select:'+E.Message, 'Error_SaveRawSource.txt');
end;
//on
end;
//try
//插入文章基本信息
adoq2.Close;
adoq2.SQL.Clear;
sSql2 := 'insert into information(arti_web,arti_id,i_address,i_content_raw,i_done) values('+s_WebID+','+sPageID+','''+sPageAddr+''',''LXQ'',0)';
adoq2.SQL.Add(sSql2);
//插入原文
adosp.Parameters.ParamByName('@id_value').Value := StrToInt(sPageID);
adosp.Parameters.ParamByName('@insert_cont').Value := sBody;
adosp.Parameters.ParamByName('@web_id').Value := StrToInt(s_WebID);
adocn2.begin
Trans;//开始事务
try
adoq.ExecSQL;
adoq2.ExecSQL;
adosp.ExecProc;
adocn2.CommitTrans;//提交事务
except
on E: Exceptiondo
begin
adocn2.RollbackTrans;//回滚事务
WriteLog('['+s_WebID+']Trans:'+E.Message, 'Error_SaveRawSource.txt');
end;
//on
end;
//--------------------------------------------------------------------------//
adoq.Close;
adoq.Free;
adoq2.Close;
adoq2.Free;
adosp.Free;
end;
ReleaseMutex(hMutex);
end;

 
这里有我写的一个简单的例子:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1239764
 
都没人了所?
 
有必要那么麻烦呀
换一种方法
不行呀
自己和自己过不去
 
CHJ天问:
那你有什么好办法可以解决“死锁”?期待你的方法!
 
谢谢回答问题的朋友,现在问题已经解决。
 
一个线程内没必要用互斥吧,用临界区就可以搞定同时写所造成的冲突
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
后退
顶部