How to program the number of your threads in Delphi

I found this on the Dr Dobbs website today at http://www.ddj.com/hpc-high-performance-computing/220300055?pgno=3 This is a good suggestion regarding thread implantation. What is the best way to achieve this with TThread in Delphi? thanks Brian

=== From Dr. Dobbs ===============

Make multithreading customizable! The number of threads used in the program should always be configured from 0 (without any additional threads) to an arbitrary number. This not only allows you to configure optimal performance, but it is also a good tool for debugging, and sometimes a lifesaver, when unknown race conditions arise on client systems. I remember more than one situation where clients were able to overcome fatal errors by disabling multithreading. This, of course, applies not only to multi-threaded file I / O.

Consider the following pseudo code:

int CMyThreadManger::AddThread(CThreadObj theTask) { if(mUsedThreadCount >= gConfiguration.MaxThreadCount()) return theTask.Execute(); // execute task in main thread // add task to thread pool and start the thread ... } 

This mechanism is not very complicated (although it will probably take a little more work than shown here), but sometimes it is very effective. It can also be used with pre-built stream libraries such as OpenMP or Intel Threaded Building Blocks. Given the measurements given here, it is recommended that you enable more than one custom thread counter (for example, one for file I / O and one for basic CPU tasks). By default, it should probably be 0 for file I / O and the number of cores found> for CPU tasks. But all multithreading should be removable. A more complex approach may even include some code for testing multi-threaded performance and setting the number of threads used automatically, maybe even individually for different tasks.

====================

+6
multithreading file-io delphi
source share
4 answers

I would create an abstract TTask class. This class is designed to perform a task. Using the Execute Method:

 type TTask = abstract class protected procedure DoExecute; virtual; abstract; public procedure Execute; end; TTaskThread = class (TThread) private FTask : TTask; public constructor Create(const ATask: TTask); // Assigns FTask and enables thread, free on terminate. procedure Execute; override; // Calls FTask.Execute. end; 

The Execute method checks the number of threads. If max is not reached, it starts the thread using TTaskThread, which calls DoExecute and as such performs the task in the thread. If max is reached, DoExecute is called directly.

+5
source share

Gamecat's answer is good regarding the class of abstract tasks, but I think calling DoExecute() on a task in the calling thread (like the article itself too) is a bad idea. I will always queue tasks that will be performed by background threads if streaming has not been completely turned off, and here's why.

Consider the following (far-fetched) case when you need to perform three independent procedures related to the processor:

 Procedure1_WhichTakes200ms; Procedure2_WhichTakes400ms; Procedure3_WhichTakes200ms; 

For the best use of your dual-core system, you want to execute them in two threads. You would limit the number of background threads to one, so with the main thread you have as many threads as there are cores.

Now the first procedure will be executed in the workflow, and it will complete in 200 milliseconds. The second procedure will start immediately and will be executed in the main thread, since one configured workflow is already taken and will be completed in 400 milliseconds. Then, the last procedure will be performed in a workflow that has already slept for 200 milliseconds, and will complete in 200 milliseconds. The total execution time is 600 milliseconds, and in 2/3 of that time, only one of both threads actually did significant work.

You can change the order of procedures (tasks), but in real life it is probably not possible to know in advance how much time each task will take.

Now let's look at a general way to use a thread pool. According to the configuration, you must limit the number of threads in the pool to 2 (the number of cores), use the main thread only for scheduling threads in the pool, and then wait for all tasks to complete. With the above task queue, the 1st thread will perform the first task, the second thread will take the second task. After 200 milliseconds, the first task will be completed, and the first worker thread will take the third task from the pool, which will then be empty. After 400 milliseconds, the second and third tasks will be completed, and the main thread will be unlocked. The total time to complete is 400 milliseconds, with 100% load on both cores during this time.

At least for processor-related threads, it is vitally important to always have a queued job for the OS scheduler. Calling DoExecute() in the main thread interferes with this, and should not be done.

+4
source share

Usually I only have one class inheriting from TThread that takes "work items" from a queue or stack and pauses them when there are no more items available. Then the main program can decide how many instances of this thread need to be created and run. (using this configuration value).

This work queue must also be smart enough to resume suspended threads or create a new thread when necessary (and when it resolves it), when a work item has been queued, or the thread has finished processing the work item.

0
source share

My structure allows thread pool counting for any of the threads in the configuration file if you want to see ( http://www.csinnovations.com/framework_overview.htm ).

0
source share

All Articles