Multithreading DelphiXe7如何处理并行任务中的多线程
我是并行编程新手,正在试图找出为什么我偶尔会遇到EmonitorLockException:当我增加要运行的并行任务数时,对象锁不属于我。我运行的任务越多,线程就越纠结。还是我的代码不正确Multithreading DelphiXe7如何处理并行任务中的多线程,multithreading,delphi,parallel-processing,task,Multithreading,Delphi,Parallel Processing,Task,我是并行编程新手,正在试图找出为什么我偶尔会遇到EmonitorLockException:当我增加要运行的并行任务数时,对象锁不属于我。我运行的任务越多,线程就越纠结。还是我的代码不正确 {$APPTYPE CONSOLE} uses System.SysUtils, System.Threading, System.Classes, System.SyncObjs, System.StrUtils; const WorkerCount = 10000; // this is the n
{$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.
您可以访问大量共享的全局变量,而无需任何同步。例如:
if z = letters[counter] then
begin
copyarray[counter] := z;
counter := counter + 1;
// increase counter when successfully found a match
end
这里的copyarray
是一个在所有任务之间共享的全局数组。仅此数据竞争就可能导致您看到的错误。还有其他类似的种族。我确信还有更多的问题。无法回收整个代码。你需要扔掉它,重新开始
以下是一些提示:
随机化。它不是线程安全的。在启动时调用它一次
Random
不是线程安全的。查找线程安全的PRNG,或同步对Random
的调用。前者是首选方案TThread。从主线程同步
try
和finally
来保护其生命周期。不要在一个函数中创建对象,而在另一个函数中销毁它们哎哟,你应该试着正确格式化代码。这将对可读性产生巨大的影响。为什么要从主线程调用TThread.Synchronize?我认为这将有助于同步线程,使它们不会被锁定不,这不是它的作用。你必须了解这个领域才能有成功的希望。并行编程比串行编程难几个数量级。@EProgrammerNotFound,请不要以“改进格式”的名义删除运算符周围的空格许多人发现空格提高了可读性。作为对David回答的回答,如果我将copyarray放在findsequencex函数中,它会有所不同吗?您需要扔掉所有代码。这毫无意义。你不能从中得到任何有用的东西。对于迄今为止所说的话,我感到非常失望。并行编程的概念在delphi中不是新概念,而是新概念。我想我会尝试通过一个有趣的例子来学习这一点,而不是通常的寻找素数。尽管我可能有点不知所措,但仅仅说扔掉代码,重新开始是没有建设性的。我从哪里开始。一些有用的指针会很有用。@JimmyDean在处理多个线程时,您需要从一开始就以不同的方式思考。通常,最好的办法是后退一步,然后重新开始。@JimmyDean并行编程在Delphi中并不新鲜。有一些新特性使某些问题的代码更加简洁;但基本上,自从创建线程以来,您就能够进行并行编程。无论您使用什么库/语言功能,您必须理解的概念问题都是相同的。您需要了解以下内容:封装、如何管理复杂性和关注点分离,然后再考虑并行编程特有的复杂性。例如:并发、竞争条件、死锁、同步、事件