Important: as a general rule, you should write your application in a such way that it will behave correctly without EurekaLog on board. This means that if your or 3rd party code throws an exception in a thread - it must be handled in a way that you expect. For example, by showing an error message, canceling action, retrying, etc. Once your application behaves like it is supposed to do - then you add EurekaLog for it. EurekaLog will auto-handle all unhandled exceptions automatically (by hooking few well-known places), and for everything else - you can call EurekaLog from your own handlers when needed. In other words:
A thread pool is a collection of worker threads that efficiently execute asynchronous callbacks on behalf of the application. The thread pool is primarily used to reduce the number of application threads and provide management of the worker threads.
Delphi and C++ Builder do not have any implementations for thread pools out of the box. Applications that want to use thread pool may utilize system API (which is most commonly used in the form of QueueWorkItem function) or frameworks. The specifics are different for each API/framework, but there is a common scheme: you call some "create" function and pass your function as argument (a callback) - which is called a "task". This action will schedule your function (i.e. callback/task) to be executed by some thread from thread pool at the specified moment. An important moment here is that you don't know which thread will run your code. Thread for each task is selected (assigned) by thread pool manager. Primary task of thread pool is re-using threads to avoid performance penalty for creating and terminating threads. This means that single thread from thread pool will execute many different tasks. If you schedule same task multiple times - there is no guarantee that your task will be executed by the same thread from thread pool.
This means that arbitrary code is not safe for thread pooling. Task function (and all functions called from task function) must be thread-pool safe. A safe function does not assume that the thread executing it is a dedicated or persistent thread. In general, you should avoid using thread local storage or making an asynchronous call that requires a persistent thread, such as the RegNotifyChangeKeyValue function. A normal rule of thumb for thread pool tasks: do not change thread state. For example, you should not terminate thread, you should not alter FPU state, you should not alter thread priority, you should not let exceptions escape your task function, etc. So, if you need to perform non-thread-pool safe action - you need to revert it before returning control from your task function.
An example of a good thread-pool function:
function MyTask(lpThreadParameter: Pointer): Integer; stdcall; IsMultiThreaded := True;
// Execute MyTask in a background thread
See also: how to handle an exception.
Since a recommended approach is to manually control activation of EurekaLog per each thread - we suggest you to use such code:
function MyTask(lpThreadParameter: Pointer): Integer; stdcall;
// Execute MyTask in a background thread
See also: how to handle an exception.
This sample code enables EurekaLog (and sets thread name) only for the duration of our task. When our task is completed, thread will be released into thread pool - so we disable EurekaLog (because we don't know which task will use this thread in the next time).
Important note: EurekaLog has to be enabled for background threads. And the same "thread persistent state" rule applies to EurekaLog per-thread state: you should clean after your task - as indicated in the above example.
Some thread pool implementations may have less restrictive rules. For example, thread pool implementations in Delphi/C++ Builder frameworks will probably have exception handling code established. See working with multithreading frameworks for more information.
See also:
|