-3

I have a problem with number of iterations in Tparallel.for. i have 100 folder and in each folder exist a file to be run (run.bat). After run the out.txt file is created in folder.when i use Tparalle.for with 100 iterations, i recieve randomly 90 to 98 out.txt while it be 100. my code is as below (Delphi XE7):

TParallel.For(1, 100, procedure(i: integer)
    begin
      SetCurrentDir(path + '\test\' + IntToStr(i));
      WinExec32AndWait(PChar('run.bat'), 0);
    end);
Kromster
  • 7,181
  • 7
  • 63
  • 111
AMIR
  • 11
  • 1
  • Sorry, but I don't understand what's the problem? What are you expecting and what's the real outcome? Might also like to look at: http://stackoverflow.com/help/asking – Andrej Jul 09 '16 at 06:53

2 Answers2

3

The process working directory is a single value for the process. You are expecting each task to have their own private copy but that is simply not how the working directory operates. In more technical terms there is a data race on the shared process wide working directory. Because of this race when the child processes are created they inherit the working directory of the parent but because of the race some children are inheriting the working directory intended for different children. This is one of the classic mistakes made with parallel programming.

Deal with this by avoiding use of the parent process working directory. Do not modify it at all. Instead pass the working directory to each child process as you create them. This can be done quite simply using CreateProcess or ShellExecuteEx. Your WinExec32AndWait function may need to be modified to accept a working directory argument.

This solves the problem by ensuring that you create a separate copy of the working directory for each distinct child process.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • WinExec32AndWait function is as below: – AMIR Jul 09 '16 at 09:35
  • function WinExec32AndWait(const Cmd: string; const CmdShow: Integer): Cardinal; var StartupInfo: TStartupInfo; ProcessInfo: TProcessInformation; begin Result := Cardinal($FFFFFFFF); FillChar(StartupInfo, SizeOf(TStartupInfo), #0); – AMIR Jul 09 '16 at 09:36
  • StartupInfo.cb := SizeOf(TStartupInfo); StartupInfo.dwFlags := STARTF_USESHOWWINDOW;//STARTF_USESHOWWINDOW//NORMAL_PRIORITY_CLASS; StartupInfo.wShowWindow := CmdShow; – AMIR Jul 09 '16 at 09:36
  • if CreateProcess(nil, PChar(Cmd), nil, nil, false,HIGH_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo) then begin WaitForInputIdle(ProcessInfo.hProcess, INFINITE); if WaitForSingleObject(ProcessInfo.hProcess, INFINITE) = WAIT_OBJECT_0 then begin if not GetExitCodeProcess(ProcessInfo.hProcess, Result) then Result := Cardinal($FFFFFFFF); end; – AMIR Jul 09 '16 at 09:37
  • CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); end; end; – AMIR Jul 09 '16 at 09:37
  • 4
    I'm actually not terribly interested in writing your program for you. What I've attempted to do here is explain **why** your code fails and **what** you should do to fix it. I'm here to help people learn. If you want to learn, great. If you don't want to learn, and just want somebody to feed you code, I am the wrong person for you. – David Heffernan Jul 09 '16 at 09:45
  • 2
    @AMIR if you have additional informations for your question then you should edit the question. Do not flood us with code in comments - noone is going to puzzle that comment parts together – Sir Rufo Jul 09 '16 at 09:57
  • Finally i solved my problem. For determining current directory, i used CreateProcess instead setcurrentdir. CreateProcess function used in WinExec32AndWait. CreateProcess(nil, PChar(Cmd), nil, nil, false,HIGH_PRIORITY_CLASS, nil, Currentdir, StartupInfo, ProcessInfo) – AMIR Jul 11 '16 at 10:36
  • Which is exactly what I said – David Heffernan Jul 11 '16 at 10:38
0

setcurrentdir is off course not multithread! what if 2 thread at the same time set the current dir to xxx ? :)

do instead

Tparallel.for(1,100.procedure(i:integer)
begin
  winexec32andwait(pchar(path+'\test\'+inttostr(i) + '\run.bat'),0);
end);
Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
zeus
  • 12,173
  • 9
  • 63
  • 184
  • Probably need to set the working directory in the child process too – David Heffernan Jul 09 '16 at 08:12
  • if use winexec32andwait without setcurrentdir , run.bat file would not be run. – AMIR Jul 09 '16 at 08:24
  • @AMIR SetCurrentDir sets the **global** current dir for the current process. After that you start the process with the batch. You forget about multithreading aspect. task1 set current dir, task2 set current dir, task1 starts the batch (oops, on wrong current dir) - that is your problem – Sir Rufo Jul 09 '16 at 12:06
  • @AMIR I added a '\' before 'run.bat'. See above. Please try again. Assuming you have '\test\1\run.bat', etc. – Rudy Velthuis Jul 09 '16 at 12:55