Delphi XE7如何处理并行任务中的多线程

时间:2015-03-11 19:12:40

标签: 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 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.

1 个答案:

答案 0 :(得分:9)

您可以在不进行任何同步的情况下访问大量共享的全局变量。例如:

  if z = letters[counter] then
  begin
    copyarray[counter] := z;
    counter := counter + 1;
    // increase counter when successfully found a match
  end

此处copyarray是在所有任务之间共享的全局。仅此数据竞争可能会导致您看到的错误。还有其他类似的比赛。我相信还有很多问题。整个代码无法挽救。你需要扔掉它然后重新开始。

以下是一些提示:

  1. 选择一个更简单的任务来开始学习并行编程。
  2. 找一本关于这个主题的好书或教程。从小处着手,解决您的实际问题。
  3. 停止使用全局变量。与全局变量共享数据只会导致痛苦。分享是你的敌人。保持最低限度。
  4. 如果您需要以显式方式共享数据,而不是使用全局变量。并确保对共享数据的访问是同步的。
  5. 请勿在帖子中调用Randomize。这不是线程安全的。在启动时调用一次。
  6. Random不是线程安全的。查找线程安全的PRNG,或同步到Random的呼叫。以前的选择是首选。
  7. 不要从主线程中调用TThread.Synchronize
  8. 以标准方式管理生命。创建对象时,请使用tryfinally来保护其生命周期。不要在一个函数中创建对象并在其他函数中销毁它们。
  9. 格式化代码,使其可读。如果您无法阅读代码,您将如何理解它?
  10. 在尊重最大的情况下,很明显你还没有掌握串行编程。在转向并行编程之前,您的目标应该是精通串行编程。并行编程至少要高一个数量级。
  11. 考虑到这一点,尝试以串行形式编写一个好的,干净的程序版本。然后考虑如何将其转换为并行版本。