PC SOFT

FORUMS PROFESSIONNELS
WINDEVWEBDEV et WINDEV Mobile

Accueil → WINDEV 2024 → Work with threads
Work with threads
Débuté par Arekusei Timakobu, 27 jan. 2015 17:21 - 22 réponses
Posté le 27 janvier 2015 - 17:21
There is a main UI thread. It starts 2 "subthreads". One is used to show Motion JPEG on Image control of UI thread and other is used for getting some other information and displaying it on UI too. Below sample code:

Main thread (UIWindow)
Event(ProcessImageFrame,UIWindow..Name,"ProcessImageFrame")
Initialize Code: (started when window is opened)
ThreadExecute("Thread2",threadNormal,UpdateServerInformation)
BTN_Play_Click (started when button is clicked)
ThreadExecute("Thread1",threadNormal,ProcessMedia)
PROCEDURE ProcessImageFrame(uMessage,wParam,lParam) // code for create Image and display it Multitask(-100) ThreadSendSignal("Thread1")
Thread 1

PROCEDURE ProcessMedia WHILE True // reading HTTP response with image raw content PostMessage(Handle(UIWindow),"ProcessImageFrame",Content,Length) ThreadWaitSignal() Multitask(-100) END
Thread 2

PROCEDURE UpdateServerInformation WHILE True // getting server information // display on UI Multitask(-100) END

The problem is when I click button and "Thread 1" is executing ("Thread 2" is already working") then "Thread 2" is stopped and no server information is updated. The main thread works only with "Thread 1", i.e. data is read and posted to main window via PostMessage, then this thread waits for a signal from UI thread. In the main thread those data is prepared to display and is displayed and send signal to "Thread 1" to continue... And so on. "Thread 2" is silent <img src="/NG2013_WEB/ui/smiley/2.gif" align=absmiddle border=0 alt=":("> When "Thread 1" is stopped then "Thread 2" continue its work. Could someone help me? Or give advise how to work with threads correctly? Thanx.
Posté le 27 janvier 2015 - 17:46
Hi Arekusei,

I think that all this is a bit complicated for a simple task. You could use only one thread. In that thread you look to get status information. When status changes (when you have an image), then simply call the main form procedure to update the status using ExecuteMainThread(). You should be in business very simply.

In fact, you could even use an automatic thread (timer option in the procedure - top-right on the procedure name header line) to do this task.

It's not that this is not possible to do it they way you did, but by what I can see, having no source code, I think you do not understand well the threading model and the use of signals. (I might be wrong, but the above code does not look good in the logic nor in the use. I might also not really understand what you try to achieve and so why this is structured the way you tried to do.)

Best regards,
Alexandre Leclerc
Posté le 27 janvier 2015 - 19:01
Quote
Alexandre Leclerc

I think that all this is a bit complicated for a simple task. You could use only one thread. In that thread you look to get status information.
I think you are right in this case but the fact is that I need to read HTTP media stream, i.e. I get request to HTTP and in result I get a multipart data:

Content-Type: multipart/mixed; boundary=--SomeBoundary

--SomeBoundary
Content-Type: image/jpeg
Content-Length: 1209
..... data....

--SomeBoundary
Content-Type: image/jpeg
Content-Length: 1209
..... data....

This stream is MUST BE read in infinite loop in SEPARATE thread otherwise my application (MainThread) will be blocked, agree? Also I use automation procedure for UpdateServerUsage but I had a problem. When timeout has expired and automated procedure starts to work the UI thread is died. So I noticed that automated procedures does not "live" with thread together.
Posté le 27 janvier 2015 - 19:25
I did a presentation for WXLive on October 10th of 2014 where I demonstrated multithreading with UI updates using the example of a ticket booth. I use queue's to handle the communication between threads. You might find something useful in that presentation. The WXLive stuff has been migrated to Youtube. Search for WXLIVE MULTITHREAD.

As stated above, it's a complicated topic.

Stewart Crisler
Posté le 27 janvier 2015 - 19:29
Hi Arekusei,

The http result can be read in the same thread that makes the request. If you want, you can indeed call another thread to manage the data. I just tried to make is simpler for you. When you start working with thread and you do not completely know how the mechanic is working, you can make a lot of messy code. So you start very simple (even if not optimal) and then when it works fine, you can try to add complexity.

The main thread will be "blocked" if it has a lot of data to parse. In you example, the same thread that receives the http request could also extract and interpret the data when there is data to process. Then it could save that data somewhere, call the main thread to display the data and loop again to wait for new data to come in. This would be very good like that.

Automatic procedure, if you select Thread, will not cause any "lock" in your main thread. But you have less control over the process in this way. In you case, the process could start right after application start and would be good enough I guess.

Based on what you described, there is probably a coding problem in how your threads are made or are working to have the problems you describe. For example, maybe your threading model is not configured correctly (see ThreadMode http://doc.pcsoft.fr/en-US/?3077028). Many little things to look at. Threads a great but not easy to get at first. You probably already read the help, but the more I read, the better I understand, usually: <a class="ExternalLink" rel="nofollow" target="_blank" href="http://doc.windev.com/en-US/?3077004">http://doc.windev.com/en-US/&hellip;</a>

Best regards,
Alexandre Leclerc
Posté le 27 janvier 2015 - 21:33
Quote
Alexandre Leclerc

The http result can be read in the same thread that makes the request.
Maybe I explain badly... My http result have no limited value in bytes. I.e. when I asked some HTML page, yes - there is no problem. I just read 100 or 1000 bytes but in my case I get infinite data stream (jpeg frames or even video stream) that I need to display on fly. Imagine water stream, you can't force it to stop, you can just use bucket fill it and "process it". But if you will use bucket by himself you can't do anything else, so you need someone (another thread) to help you. The code is looks like:

let request <- HttpWebRequest.Create(URL) let response = request.GetResponse() let responseStream = response.GetResponseStream() let bufferedStream <- new BufferedStream(responseStream) WHILE True // get a little portion of bytes to get multipart header // read the value of Content-Length http header arrImage is array [1024] of 1-byte unsigned int bufferedStream.Read(arrImage,0,arrImage..Occurence) // convert arrImage (Array) into Buffer bufImage is Buffer = GetBufferFromArray(arrArray) cImage is Image = dLoadImage(bufImage) // OK! Image is ready! We need to display it! // ... END If I will use this code in main UI thread, obviously I can't do nothing in my main window but I need click on tabs, I need update server usage information etc. Correct? This is infinit loop! And I need to process other window or control events... So the separate thread is required.

Quote
Alexandre Leclerc
I just tried to make is simpler for you.
Thank you for care but we don't find easy ways <img src="/NG2013_WEB/ui/smiley/1.gif" align=absmiddle border=0 alt=":)">

Quote
Alexandre Leclerc
When you start working with thread and you do not completely know how the mechanic is working, you can make a lot of messy code.
I imaging how threads works.. I hope :) It was easy to do in C#... I just create a thread, declare event, subscribe on it in main window and raise it in the running second thread. The same thing I need in WinDev. I need to process infinite data (motion JPEG or MPEG4 data) that is coming from HTTP server and I need to process it so my UI have not died and user can do other actions. If WinDev would support asynchronous operations it would be good but I didn't find anything on this subject in help.

Quote
Alexandre Leclerc
The main thread will be "blocked" if it has a lot of data to parse.
Exactly! Moreover data stream can't come evenly. It is Internet and some delays are possible.

Quote
Alexandre Leclerc
Then it could save that data somewhere, call the main thread to display the data and loop again to wait for new data to come in.
It is imposible! If I need to display video I can't save data somewhere I need to stream it on player. And let's take a picture of HD quality 800x600 with 75%. Its size more the 1Mbyte and the playback can continue enough long time. What HDD size do I need to have? :) This variant is not acceptable.

Quote
Alexandre Leclerc
Automatic procedure, if you select Thread, will not cause any "lock" in your main thread.
So why do I need automatic procedure if I can use threading? ;)

Quote
Alexandre Leclerc
Based on what you described, there is probably a coding problem in how your threads are made
That is why I have written here :)

I repeat all that I need:
1. Process pemanently coming data in secondary thread
2. Update window controls on main thread with data that is received in secondary thread.
3. Periodically request server and update server usage info on the main window (main thread)
4. These two processes must not block the UI (main) thread, i.e. user have ability to click buttons, tabs and do any other actions.
Posté le 27 janvier 2015 - 21:41
Btw, below a sample code on C# (cut). Here it is the example of what I need.

// create and start new thread thread = new Thread(new ThreadStart(WorkerThread)); thread.Name = source; thread.Start(); // Thread entry point public void WorkerThread() { byte[] buffer = new byte[bufSize]; // buffer to read stream while (true) { // create request req = (HttpWebRequest) WebRequest.Create(source); // set login and password if ((login != null) && (password != null) && (login != "")) req.Credentials = new NetworkCredential(login, password); // get response resp = req.GetResponse(); // check content type string ct = resp.ContentType; if (ct.IndexOf("multipart/x-mixed-replace") == -1) throw new ApplicationException("Invalid URL"); // get boundary ASCIIEncoding encoding = new ASCIIEncoding(); boundary = encoding.GetBytes(ct.Substring(ct.IndexOf("boundary=", 0) + 9)); boundaryLen = boundary.Length; // get response stream stream = resp.GetResponseStream(); // loop while ((!stopEvent.WaitOne(0, true)) && (!reloadEvent.WaitOne(0, true))) { // read next portion from stream if ((read = stream.Read(buffer, total, readSize)) == 0) throw new ApplicationException(); ... some code ... // image at stop (if event is subscribed) if (NewFrame != null) { Bitmap bmp = (Bitmap) Bitmap.FromStream(new MemoryStream(buffer, start, stop - start)); // notify client (send event to) NewFrame(this, new CameraEventArgs(bmp)); // release the image bmp.Dispose(); } } }
Posté le 27 janvier 2015 - 21:57
Hi again Arekusei,

Ok, now I understand better where you are going. I still do not get how your processes are split. Is one thread get HTTP responses and another managing it's data? If so why can't one do it all? If not, how are you passing the data between both thread? Or are you using multiple threads to process the data? Finally, how do you ask the main form to display the image? But maybe all this is working already. (There are many parts that work together and in many of them something can go wrong.)

But to come back to your original question, did you check the threading model? I had troubles in the past because it was no right. I'm quite sure this is due to that, or a maybe a wrong use of the ThreadWaitSignal()... in fact, what signal is your thread waiting before processing more data?

If the threading model is right, you can have multiple threads working independently or together without blocking each other. The only blocking you can have is a thread waiting for something never happening (like a signal that never comes in or has already passed by, etc.)

I hope this can help you a little bit. Sorry if not.

Best regards,
Alexandre Leclerc

PS. My comment about automatic procedures is that they can be threads, but managed by WinDev (so less control to the programmer, but easier to use). Indeed you do not need them if you work directly with thread.
Posté le 27 janvier 2015 - 22:04
Hi Arekusei,

The same thread is doing the HTTP request and also processing the data, then sends an image to the main application for display. This is what I was trying to tell in my first message. One thread does all that.

I guess all the display part is working well.

This thread should work well and the other thread that sends the status information should also work in parallel at the same time. Just make sure the threading model is good.

Kind regards,
Alexandre Leclerc
Posté le 28 janvier 2015 - 08:46
Hi Alexandre.

I don't understand why you don't understand <img src="/NG2013_WEB/ui/smiley/1.gif" align=absmiddle border=0 alt=":)">

Quote

The same thread is doing the HTTP request and also processing the data, then sends an image to the main application for display.
How?! Imaging... a window... on windows message loop...

The simple (sample) code:

PROCEDURE WIN_MAIN Info("The window has been opened. After click button OK the procedure ProcessMediaStream will be run!") ProcessMediaStream() // the code for readin of data stream Info("The ProcessMediaStream is running. User can click any buttons and other controls. User can move and resize window") PROCEDURE ProcessMediaStream() WHILE True // infinite loop! ... END

Please, explain me how I can get the second Info message, how can I click on buttons etc if the procedure ProcessMediaStrem contains infinite(!) loop?! This procedure will not return control to application after its completion, i.e. never because I repear the loop is infinite.

Quote
But to come back to your original question, did you check the threading model?
What do you mean talking about threading model? It seems I don't completely understand.

Quote
I still do not get how your processes are split. Is one thread get HTTP responses and another managing it's data? If so why can't one do it all?

I have already explained it again above.. ;) The main thread is the main window that must be accessible at any time(!). User must click tabs, buttons, information in textboxes must be changed with some defined time interval. How it may be possible (using your suggestion with single thread) if main window calls the procedure with infinite loop? That it is why I need the second thread that reads the data independently! I.e.

PROCEDURE WIN_MAIN Info("The window has been opened. After click button OK the procedure ProcessMediaStream will be run!") ThreadExecute(ProcessMediaStream,threadNormal,"ProcessMediaStream") // the code for reading of data stream Info("The ProcessMediaStream is running. User can click any buttons and other controls. User can move and resize window")
In this code I see the second Info message and the data will be read in secondary thread and those infinite loop will not affect on my UI (main) thread. Am I right?

ThreadWaitSignal and ThreadSendSignal I use because I can't read next data portion from data stream if the previous data hast not be displayed on Image control of main window (main UI thread).

I would like to see your example of code that you suggest but the single thread can't serve my functionality w/o variants.

P.S. All that I need if someone explain me on example how can I use 2 (or 3 threads) for: 1.having total control of main UI thread (w/o blocking),
2.having ability to read pemanently coming data, process it and display on main window (asynchronously)
3. having ability to make requsts to server and display other data on the main window asynchronously.

The Trace statement must be log something like this:

Proc1 - Getting image data... wait
Proc2 - Displaying image data...
Proc1 - Getting image data... continue
Proc1 - Getting image data... wait
Proc2 - Displaying image data...
Proc1 - Getting image data... continue
Proc3 - Getting server information data...
Proc1 - Getting image data... wait
Proc2 - Displaying image data...
Proc1 - Getting image data... continue
Proc1 - Getting image data... wait
Proc2 - Displaying image data...
Proc3 - Getting server information data...
Posté le 28 janvier 2015 - 09:40
Well.. I leave only main thread and the secondary thread. Secondary thread read data and PostMessage to main window to display prepared image. I've removed ThreadWaitSignal and ThreadSendSignal, i.e. I've done as in above C# example. All works fine... but I need asynchronously to make additional request to display server usage info. If I make the requesting procedure as automated procedure with some timeout then when this procedure starts to work my main window is dying... i.e. there is a problem with threads and automated procedures (I think it is linked with timer). So how can solve this problem? Do I need to use another thread?
Posté le 28 janvier 2015 - 09:58
Lets' continue.. I created an automared procedure UpdateServerData that is running in a thread.

PROCEDURE UpdateServerData() WHILE True gServerData = RequestServerData() ExecuteMainThread(DisplayServerData) Multitask(-500) // delay 5 sec with returning control to windows and WDLanguage END PROCEDURE DisplayServerData() EDT_GlobalMemory = gServerData.p_sGlobalMemory // display data ...
When I only start the main thread the UpdateServerData is working fine (data is refreshed). As I only start thread with readin of stream video data the automated procedure UpdateServerData is stopped. <img src="/NG2013_WEB/ui/smiley/2.gif" align=absmiddle border=0 alt=":(">

Please help me to synchronize threads...
Posté le 28 janvier 2015 - 15:38
Hi Arekusei,

Please, take the time to do a very simple test project. Create an empty form, put a Multi-Line Text control in it and make it bid. Then put the following code in the form:


// In the declaration of the window: PROCEDURE MyWindow() StartThread1() StartThread2() // Add a procedure named as follow // Configure the procedure (timer option) to run as a THREAD WITHOUT HFSQL needs // // Automatic Procedure: // The procedure is performed manually during a call in the code // It will run in a thread (without needing to call the ThreadExecute function) without using HFSQL // PROCEDURE StartThread1() WHILE True ExecuteMainThread(Display,"Thread 1 " + Now()) ThreadPause(50) END // Add a procedure named as follow // Configure the procedure (timer option) to run as a THREAD WITHOUT HFSQL needs // // Automatic Procedure: // The procedure is performed manually during a call in the code // It will run in a thread (without needing to call the ThreadExecute function) without using HFSQL // PROCEDURE StartThread2() WHILE True ExecuteMainThread(Display,"Thread 2 " + Now()) ThreadPause(70) END // Then add the following standard procedure: PROCEDURE Display(s is string) SAI_SansNom1 += [CR] + s // SAI_SansNom1 is the multi-line text control
Now I run this code and both thread are working well together without blocking the main interface (main thread). This is a basic thread experience. Then from there you add complexity.

Your above project should be as simple as that to work. StartThread1 is the thread that get and process web data to display images. StartThread2 is the one sending or getting server status.

If you want to use manual thread, that is fine. But start simple, it will help you identify the problem in your code. I'm sorry if I do not understand what you do, maybe you do not understand what I try to express also. So I do all my best to give you tools that will help you solve your problem. I do not have your source code.

Hope this can help.

Kind regards,
Alexandre Leclerc
Posté le 28 janvier 2015 - 15:41
Hi Arekusei,

You see, some code in your code was causing issues. So my guess is that some other code is also causing issues. When you try to identify a problem, always reduce the problem to its simplest expression. This is why I asked you in another post to build a very simple thread app. Starting from there you can add complexity.

Both thread should work well together without disturbing the other. The main thread should not have blocking actions (of course).

Kind regards,
Alexandre Leclerc
Posté le 28 janvier 2015 - 15:51
Hi again Arekusei,

Quote

How?! Imaging... a window... on windows message loop...

When I speak of one thread, I do not speak of the main thread. I say that one thread is doing all the blocking job and that is it good enough. If you look at the C# code, this is exactly what it is doing.

Main thread (application): calls Thread_GetTheDataFromTheWeb() and also calls Thread_GetTheStatusAndSendStatusInfo() and the threads are calling DisplayImages() or DisplayStatus() of the main thread. Voilà!

You do not need a second data processing thread nor signals. If you want, complexify later. For now just concentrate to read the data and send images to the main application in an infinite loop, in one thread that does only that. HTTP + DataStream + Send to main application thread. Then later, when this works, just try to add some more complexities if you need to. The images could be stored in an array of images and just leave it to the main application to decide when displaying them (buffer principle).

The very small project example should answer your questions. I'm sure you will get the picture. Simple threads can do the job you want. I even thing that the data thread could also send status notifications to the server after it has received and processed the previous data. But it can be in two threads without a problem.

I really hope all this can be of some help to you.

Best regards and good luck,
Alexandre Leclerc
Posté le 28 janvier 2015 - 16:03
Quote
Alexandre Leclerc

Please, take the time to do a very simple test project.
Ok. I will try this code. How can I pass parameter to main thread to display data on main window? Or is it possible to update controls from threads (I am not sure)? I need to pass Image variable or array of int or object.
Posté le 28 janvier 2015 - 16:08
Hi Arekusei,

Read my other posts, this will answer your question. Test the above code, this will also answer your question about how to pass data. This is one way to do it (and a simple one).

In a nutshell: No you can't update main GUI from a thread. For data, just create a proc that has an image as parameter, or an integer. The above example passes a string.

Best regards,
Alexandre Leclerc
Posté le 28 janvier 2015 - 17:12
Alexandre, thank you for your help!

I've used your example and create 2 automated procedures running in threads. The single difference is that one of "long" procedure is a method of class so I've done so

WHILE True ExecuteMainThread(WIN.DisplaySomeData,nFrameCount,nFrameSize) END
The first threaded procedure is run on window start the second after button click. When I run the application the first thread is running. When I click the button to start 2nd thread this thread is running but 1st thread become waiting. At lease I don't see answer from it. When I stop 2nd thread the first thread continue its work. This is a problem <img src="/NG2013_WEB/ui/smiley/1.gif" align=absmiddle border=0 alt=":)">
Posté le 28 janvier 2015 - 17:32
Hi Arekusei,

I'm sorry but I can't help you above that since I do not know how your code is organized (threads, non-thread) and what is the code. As my example shows, two threads are not blocking each other and can work very well together.

Now, is the second thread stopping the first one by a line of code? Is it playing with signals or critical sections? A critical section can block a thread. How is called your first thread and how is called the second one? Do they have different name. Are you using critical sections and if so, is the ThreadMode() set correctly?

The "long" code is called from the thread or called to be executed by the main thread? The While True for display does not look correct to me do you mean:
WHILE True // http request // parse data ExecuteMainThread(...) // loop again to receive and parse more data END
or do you really mean:
WHILE True ExecuteMainThread(...) END
In that case, (and I don't know anything about your code), this looks wrong. Anyway.

You must identify the source of the problem by a thorough analysis and unfortunately nobody can do it for you. It should work quite simply if the code is correct.

Best regards,
Alexandre Leclerc
Posté le 29 janvier 2015 - 08:18
Hi Aleandre.

Here it is as it looks:

End of initialization WIN_MS

ProcessServerUsage() // automated procedure
BTN_Play click

ProcessImageStream(RADIO_MediaType) // automated procedure
PROCEDURE ProcessServerUsage() WHILE True gclServer = SomeAPI.GetServerInfo() ExecuteMainThread(DisplayServerUsage,gclServer) ThreadPause(300) END
PROCEDURE ProcessImageStream(MediaType) IF MediaType = "MJPEG" SomeAPI.GetMJPEG('Camera',...) END
SomeAPI is Class PROCEDURE GetMJPEG(Camera,...) // request and process response WHILE True // reading from response stream sHeader = GetMultipartHeader() // read header arrReadBuffer = StreamReader.Read(...) // read content part // some light actions ... // if content is image IF ContentTypeBase = "image" nFrameCount++ clImage is Image = GetBufferFromArray(arrReadBuffer) // use Transfer for copy array to buffer ExecuteMainThread(WIN_VMS.DisplayMediaFrame,clImage,nFrameCount) ThreadPause(50) END Trace(...) END END
PROCEDURE DisplayServerUsage(gclServer) EDT_ServerName = ... // Assign values from gclServer to screen controls Trace(...)
PROCEDURE DisplayMediaFrame(clImage,nFrameCount) IMG_MJPEG = clImage STC_FrameCount = nFrameCount Trace(...)
That's all. The method GetMJPEG is most "hard" place. It reads response stream from web server. It read about 100 bytes to process multipart header to define what content type is streamed and what the size of the multipart content. Then this size is read from stream and converted from array to buffer. After that buffer is used to load into Image variable and passed to main thread.
Posté le 29 janvier 2015 - 14:34
Hi Arekusei,

There is only one thing that makes me uncomfortable about your code and it is the use of a class object.

Here is an unverified supposition from me. (I have no specific real-world experience with this kind of code setup, so to say. You should confirm with the Free Technical Support.) I fear that the class instance might be blocking your threads because you try to execute two threads from within the same object instance. Your class methods are not GLOBAL and so WinDev might think that you want to execute two "same procedure/object" at the same time from threads. And so, only one thread at the time will be able to run. And this is exactly that behaviour you have right now.

So if that supposition is right you would have two solutions:

1 - Read the help file again and configure the ThreadMode() as I was explaining in my first posts. See "Processes of a WinDev application" in this help page: <a class="ExternalLink" rel="nofollow" target="_blank" href="http://doc.windev.com/en-US/?3077004&name=principle_for_running_threads#limits_the_processes_performed_the_thread_ELTPARAGRAPHE000325">http://doc.windev.com/en-US/&hellip;</a>

You will have to use critical sections to read and write your object's members and methods or properties that change the members. The threading model has to be set to threadCriticalSection. Then your class object will not block your both threads. (This is my supposition.)

or

2 - Make the class methods as GLOBAL procedures. I think it should, then, be considered as a normal procedure and make no problem without having to play with the ThreadMode() and critical sections. In that case, make the members you must interract with as PUBLIC GLOBAL too.

Another thing you might consider to do is simply testing your code in standard procedures instead of through a class. Then make it a class. By the way, why are you using a class object with non-global procedures for a thread? If it is to access specific config data? Then, as said in point 2, also make the required members PUBLIC GLOBAL and this will solve your problem. (I say that in case you simply use a class as a container for related procedures and common variables.)

I Hope this can finally solve your problems at once.

Best regards,
Alexandre Leclerc
Posté le 30 janvier 2015 - 11:49
Hi, Alexandre.

I re-read all you wrote me and you was right ponting me on incorrect threading mode. I've read the documentation about ThreaMode function and found out that default Automatic management of the critical sections force each procedure or method to be run by a single thread at a time. I've changed the threading mode to threadCriticalSection and now my secondary threads live in peace and harmony even without ExecuteMainThread call.

Thank you for your help!
Posté le 30 janvier 2015 - 14:27
Hi Arekusei,

I'm very glad for you that it is now working!

Now I want to recap a make a point clear:

- Both of your threads would have worked in perfect harmony if they were not considered to access the same "object" or "method" at the same time. (The proof is in the small test application I posted up there. And it looks like that my previous supposition was right since the ThreadMode() has solved the issue. Something new has just been learned.)

- You had to use the ThreadMode() with threadCriticalSection because both of your thread were non-global methods in a class object. But it would have not been required if the code was structured differently.

I just wanted to point it out to make sure you perfectly understand this very important little detail. This will help you greatly going on.

Also, as additional notes:

- Make sure to use critical sections if two parts of code in threads can read/write at any given time the same object or variable to avoid read/write collisions, if this is required in your processes. (Sometimes it is not required, and will not hurt to not use critical sections.)

- Use ExecuteMainThread() when you have to interact with the UI. Note that this will block the calling thread until the action has been done by the main thread. Then the calling thread will continue. (It is not required in many cases, but in other, yes.)

Best regards!
Alexandre Leclerc