Posts: 795
Threads: 136
Joined: Feb 2009
Hi Gintaras, hi all,
1. What is the QM way to do a C like do-while
do{
......
}(while(........)
loop?
2. I have a shell menu function that does file conversion.
function $files
str f
foreach f files
int ht=mac("sub.Convert" "" f)
mac "sub.End" "" ht
It works flawlessly, but as I have a powerful laptop, I'd like to use it multithreaded, i.e it would convertĀ
many files at the same time.
Can you steer me to the solution?
Thanks
Posts: 12,092
Threads: 142
Joined: Dec 2002
1.
rep
;... ;;code without 'continue'
;if(!...) break
2. Your code already does it. mac("sub.Convert" "" f) creates new thread. Don't know what the next line does.
But if there are many files, can create too many threads and make slightly slower than when the thread count matches the count of logical CPUs. Probably not a problem in most cases.
Posts: 795
Threads: 136
Joined: Feb 2009
Well, when I select multiple files for conversion in explorer, I can certify there is only conversion at a time with this macro, that is why i'd like to have 4 threads running at the same time, my laptop can handle this heavy duty....
The last function is useless and for testing purpose only, called when last file has been converted.
Posts: 795
Threads: 136
Joined: Feb 2009
02-10-2019, 06:36 PM
(This post was last modified: 02-10-2019, 06:37 PM by ldarrambide.)
Update: this code does give as much threads as wanted for conversion routine based on r variable.
Feel free to update and improve the code Gintaras
function $files
;==========================================================================================
out
int r=0
int ht
;============================================================================================
str f
foreach f files
,r+1
,ht=mac("sub.Convert" "" f)
,if r=3
,,r=0
,,WaitForThreads 0 "MKV:Convert"
Posts: 12,092
Threads: 142
Joined: Dec 2002
Macro MKV
out
;create list of files for testing
ARRAY(str) af; GetFilesInFolder af "q:\test"
str files=af
;==========================================================================================
int nThreads=4
__Handle sem=CreateSemaphore(0 nThreads nThreads 0)
str f
foreach f files
,wait 0 H sem
,mac("sub.Convert" "" f sem)
WaitForThreads 0 "MKV:Convert" ;;to avoid calling ReleaseSemaphore(closedHandle)
#sub Convert
function $f sem
atend sub.ReleaseSem sem
;thread code for testing
MES m.timeout=RandomInt(1 3); m.x=RandomInt(1 1000)
mes f "Convert" m
#sub ReleaseSem
function sem
ReleaseSemaphore(sem 1 0)
Posts: 795
Threads: 136
Joined: Feb 2009
02-10-2019, 07:58 PM
(This post was last modified: 02-10-2019, 08:40 PM by ldarrambide.)
Thanks
I have little remembrance about semaphore use, and did not think of them.
BUT
1. why you use :
WaitForThreads 0 "MKV:Convert" ;;to avoid calling ReleaseSemaphore(closedHandle)????
2. Why use a sub function to release the semaphore (ReleaseSem) and not do it directly at the end of the convert function?
3 Why use atend and no call the sub function ReleaseSem directly at the end of the convert function?
Posts: 12,092
Threads: 142
Joined: Dec 2002
1. The main macro exits when it started the last 4 threads. When it exits, sem destructor calls CloseHandle, making the handle invalid and its numeric value subject to use for another handle. If the main thread does not wait until other threads exit, other threads call ReleaseSemaphore with invalid or another (value reused) handle.
2, 3. If you know that threads never ever return or throw exception without calling ReleaseSemaphore, then call ReleaseSemaphore at the end instead of atend/#sub. Another way to ensure that ReleaseSemaphore is always called at the end - create a class that calls ReleaseSemaphore in destructor, and declare a variable of that class in the thread.
Posts: 12,092
Threads: 142
Joined: Dec 2002
Improved version. Previous version does not work well when the main macro started again while some old threads are still not ended. Also now is used class instead of atend.
Macro MKV
out
;create list of files for testing
ARRAY(str) af; GetFilesInFolder af "q:\test"
str files=af
;==========================================================================================
__Handle+ g_sem2257; lock() if(g_sem2257=0) int nThreads=4; g_sem2257=CreateSemaphore(0 nThreads nThreads 0)
str f
foreach f files
,wait 0 H g_sem2257
,mac("sub.Convert" "" f)
#sub Convert
function $f
#compile "__SemaphoreReleaser"
SemaphoreReleaser semRel=g_sem2257
;thread code for testing
MES m.timeout=RandomInt(1 3); m.x=RandomInt(1 1000)
mes f "Convert" m
Macro __SemaphoreReleaser
class SemaphoreReleaser -_h
Member function SemaphoreReleaser.
Note: it is Member function, and its name ends with period.
Posts: 795
Threads: 136
Joined: Feb 2009
>>1. The main macro exits when it started the last 4 threads. When it exits, sem destructor calls CloseHandle, making the handle invalid and its numeric value >>>subject to use for another handle. If the main thread does not wait until other threads exit, other threads call ReleaseSemaphore with invalid or another >>>(value reused) handle.
I got it, like C++ thread function join().
2, 3. If you know that threads never ever return or throw exception without calling ReleaseSemaphore, then call ReleaseSemaphore at the end instead of atend/#sub. Another way to ensure that ReleaseSemaphore is always called at the end - create a class that calls ReleaseSemaphore in destructor, and declare a variable of that class in the thread.
I think that KISS principle makes me carry on with the old code
#sub ReleaseSem
function sem
ReleaseSemaphore(sem 1 0)
Thanks.
Posts: 795
Threads: 136
Joined: Feb 2009
Reading much since my post and your code, I see that Semaphore, mutexes and lock are individual entities in C++ and seem mixed in Qm implementation.
Can you tell me the QM way for each one?
Posts: 12,092
Threads: 142
Joined: Dec 2002
QM does not have classes for semaphores, mutexes and critical sections. In lock by default is used fast code similar to critical sections; or can be used mutex. In this macro I directly used Windows API functions CreateSemaphore, ReleaseSemaphore.
|