我是并行编程的新手,并且我试图找出为什么我偶尔会得到一个EmonitorLockException:当我增加要运行的并行任务的数量时,不拥有对象锁。是这样的情况,线程变得纠结我运行的任务越多。或者我的代码是不正确的?
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.Threading, System.Classes, System.SyncObjs, System.StrUtils;
const
WorkerCount = 10000; // this is the number of tasks to run in parallel note:when this number is increased by repeated factors of 10
// it takes longer to produce the result and sometimes the program crashes
// with an EmonitorLockException:Object lock not owned . Is it that my program is not written correctly or efficiently to run
// a large number of parallel taks and the Threads become entagled.
// Eventually I would like to optimeize the program to find the optimal number of tasks to run in parallel so as to find the result in the shortest time.
// This becomes important when the word sequence is increased to six or more letters.
sequencetofind='help'; // letter sequence to find randomly
sequencelengthplus1=5; // the ength of the letter sequence plus 1 extra letter for a check to see if it is working
var
Ticks: Cardinal;
i,k,m: Integer;
sequencenum: Integer;
alphabetarray:array[1..26] of string;
v:char;
copyarray,letters:array[1..sequencelengthplus1] of string;
tasks: array of ITask;
LTask: ITask;
Event1:TEvent;
sequencesection:TCriticalSection;
function findsequencex(index: Integer): TProc;
begin
Result := procedure
var
counter,m:integer;
r:integer;
z:string;
lettersx:array[1..sequencelengthplus1] of string;
begin
for m:=1 to sequencelengthplus1-1 do
lettersx[m]:=letters[m];
randomize;
counter:=1;
repeat
r:=random(26)+1;
z:=alphabetarray[r]; //randomly find letters until matched with the sequence
if z=letters[counter] then
begin
copyarray[counter]:=z;
counter:=counter+1; // increase counter when successfully found a match
end
else
counter:=1; // if match fails start again and look for the first letter
if (counter=sequencelengthplus1) then
begin // if all letters found in correct order find one more letter as a check
sequencesection.Acquire; //critical section start
r:=random(26)+1;
z:=alphabetarray[r];
TInterlocked.CompareExchange(sequencenum,r,0);
copyarray[sequencelengthplus1]:=z;
Event1.SetEvent; // set in motion the process to stop all other tasks
sequencesection.release; // critical section end
end;
until (Event1.WaitFor(0)=wrSignaled); // check to see if all letters of the sequence has been found
end;
end;
procedure Parallel2;
var
i,sequencevalue,j: Integer;
begin
Event1:=TEvent.Create(nil,true,false,'noname'); // sequence checker
Event1.resetevent;
sequencenum := 0;
Ticks := TThread.GetTickCount;
SetLength(Tasks, WorkerCount); // number of parallel tasks to undertake
for i := 0 to WorkerCount-1 do
Tasks[i]:=TTask.Run(findsequencex(i));
TTask.WaitForAny(Tasks); // wait for the first one to successfully finish
TThread.Synchronize(nil,
procedure
begin
for LTask in Tasks do
LTask.Cancel; // kill the remaining tasks
TInterlocked.Add (sequencevalue, sequencenum); // note the random letter check
end);
Ticks := TThread.GetTickCount - Ticks;
writeln('Parallel time ' + Ticks.ToString + ' ms, last random alphabet sequence number: ' + sequencenum.ToString+' random letter is = '+alphabetarray[sequencevalue]);
end;
begin
sequencesection:=TCriticalSection.Create;
for m:=1 to (sequencelengthplus1-1) do
begin
letters[m]:=copy(sequencetofind,m,1);
writeln(letters[m]);
end;
i:=0;
for v:='a' to 'z' do
begin
i:=i+1;
alphabetarray[i]:=v;
end;
try
begin
Parallel2; // call the parrallel procedure
writeln('finished');
for m:=1 to sequencelengthplus1 do
writeln(copyarray[m]);
if (Event1.WaitFor(0)=wrSignaled) then
begin
writeln('event signaled');
if (sequencenum=0) then writeln('sequence is null');
end;
Event1.Free;
sequencesection.free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
答案 0 :(得分:9)
您可以在不进行任何同步的情况下访问大量共享的全局变量。例如:
if z = letters[counter] then
begin
copyarray[counter] := z;
counter := counter + 1;
// increase counter when successfully found a match
end
此处copyarray
是在所有任务之间共享的全局。仅此数据竞争可能会导致您看到的错误。还有其他类似的比赛。我相信还有很多问题。整个代码无法挽救。你需要扔掉它然后重新开始。
以下是一些提示:
Randomize
。这不是线程安全的。在启动时调用一次。Random
不是线程安全的。查找线程安全的PRNG,或同步到Random
的呼叫。以前的选择是首选。TThread.Synchronize
。try
和finally
来保护其生命周期。不要在一个函数中创建对象并在其他函数中销毁它们。