Multithreading DelphiXe7如何处理并行任务中的多线程

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

我是并行编程新手,正在试图找出为什么我偶尔会遇到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.

您可以访问大量共享的全局变量,而无需任何同步。例如:

  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中并不新鲜。有一些新特性使某些问题的代码更加简洁;但基本上,自从创建线程以来,您就能够进行并行编程。无论您使用什么库/语言功能,您必须理解的概念问题都是相同的。您需要了解以下内容:封装、如何管理复杂性和关注点分离,然后再考虑并行编程特有的复杂性。例如:并发、竞争条件、死锁、同步、事件