多個執行緒Thread用dbExpress元件存取資料庫之交易管理問題 |
尚未結案
|
apl
一般會員 發表:5 回覆:4 積分:1 註冊:2003-04-08 發送簡訊給我 |
(*
說明: 以下程式表示每個Thread持續自資料庫讀取屬於該用戶之最早10筆資料並加以處理。每個用戶會有2個以上之Thread, 要求Thread在讀取資料時, 不可重複讀取同一用戶之其它Thread正在處理之資料。 作業環境: Delphi7.0(on Windows2000 sp3) + MS SQL Server 2000(on Windows2000 sp3) DB元件: dbExpress元件如下:
dbxconn: TSQLConnection;
dbxq: TSQLquery;
dbxsds: TSimpleDataSet; 程式碼: 如下
*)
procedure TMsgThread.ThreadProc;
var
sSql: string;
aTD: TTransactionDesc;
i, noMsg, nLen, myId: Integer;
m: array[0..9] of TRecordMsg; // msg array
binData: array of Byte; // byte array
bProcessMsgOk: Boolean;
begin bProcessMsgOk := False;
noMsg := 10; // 產生一組亂數(亂數值避開0/1), 用以鎖定資料
Randomize;
myId := Random(32757) + 10; // 10<=myId<32767 // 第一階段交易: 選定訊息
aTD.TransactionID := 1;
aTD.IsolationLevel := xilDIRTYREAD;
dbxconn.StartTransaction(aTD);
try
sSql := Format('select top %d * from TBL_MSG'
' where (USER_ID=%d) and (MSG_STATUS=0)'
' order by TIME_RECEIVED', [noMsg, FUserId]);
with dbxq do
begin
Close;
SQL.Text := Format('update TBL_MSG set MSG_STATUS=%d '
'from (%s) as T1 where (TBL_MSG.MSG_ID=T1.MSG_ID)',
[myId, sSql]);
ExecSQL(True);
Close;
end; dbxconn.Commit(aTD);
except
dbxconn.Rollback(aTD);
raise;
end; // 第二階段交易: 處理選定訊息
aTD.TransactionID := 1;
aTD.IsolationLevel := xilREADCOMMITTED;
dbxconn.StartTransaction(aTD);
try
sSql := Format('select * from TBL_MSG'
' where (USER_ID=%d) and (MSG_STATUS=%d)'
' order by TIME_RECEIVED', [FUserId, myId]);
with dbxsds do
begin
Active := False;
PacketRecords := noMsg;
DataSet.CommandText := sSql;
Active := True; if (RecordCount > 0) then
begin
First;
for i := 0 to (noMsg - 1) do
begin
if eof then break; // 讀出資料長度及內容
m[i].len := FieldValues['MSG_LENGTH']; binData := FieldValues['MSG_BINDATA'];
CopyMemory(@m[i].binData[0], @binData[0], m[i].len); // 修改訊息狀態
Edit;
FieldByName('MSG_STATUS').Value := 1; //設為已處理完畢 Next;
end;
ApplyUpdates(0);
Active := False; end //if (RecordCount > 0)
else
bProcessMsgOk := True; // 無訊息待處理 end; // with { 在此處理讀入之資料 ...
...
...
...
} bProcessMsgOk := True; // 表示第一階段鎖定資料已處理完畢
dbxconn.Commit(aTD); except
dbxconn.Rollback(aTD);
end; // 第三階段: 若第二階段處理失敗
if not bProcessMsgOk then
begin
aTD.TransactionID := 1;
dbxconn.StartTransaction(aTD);
try
sSql := Format(
' update TBL_MSG set MSG_STATUS=0 '
' where (USER_ID=%d) and (MSG_STATUS=%d)',
[FUserId, myId]); with dbxq do
begin
Close;
SQL.Text := sSql;
ExecSQL(True);
Close;
end; dbxconn.Commit(aTD);
except
dbxconn.Rollback(aTD);
raise;
end; end; // if not bProcessMsgOk end;
//---------------------------------------------------------------- 目前作法分三階段:
第一階段:先選定10筆資料,加上亂數標記(避免其它Thread讀取)
第二階段:讀入上述標記資料進行處理。
第三階段:若第二階段處理失敗,則恢復第一階段選定的資料。 有幾個問題:
一、若不幸同時某用戶2個以上Thread選到同一組亂數,則第二階段就出問題。
(此題可自資料庫再次查詢便可確認無重複亂數)
二、若程式中第二階段處理失敗,將進入第三階段時程式掛了,則這些資料
便無法恢復成原來狀態。(此題很難解) 不知大家有沒有辦法將上述三階段合併在一個交易中完成?
|
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |