A high performance library for Delphi to simplify coding and improve performance of asynchronous and multithreaded applications.
Inline coding helpers for implementing asynchronous actions
This unit includes two synchronization helpers that make it easy to make sure code is executed in the main thread. They automatically protect you from accidentally synchronizing from the main thread.
TAsync.SynchronizeIfInThread( procedure begin // Do something in the main thread end ); TAsync.QueueIfInThread( procedure begin // Do something in the main thread end );
Asynchronous operations can easily be performed inline in code. The global Async object may be used when lifetime of the async operations is not dependent upon another object (like a form or data module).
Async.AfterDo(1000, procedure begin // Execute this code after waiting 1s end ); Async.DoLater( procedure begin // Execute this code at some point in the very near future. end ); Async.OnDo( function : boolean begin // Check for some condition, return true if the due should be executed. False if it should not end, procedure begin // Executes only when the condition above has been met end, 1000, // interval on which the check function should be called function : boolean begin // Return True to continue checking or False to abort end ); Aync.DoEvery(1000, function : boolean begin // Execute this function every 1s. Return True to allow it to execute again or False to stop. end );
When lifetime of an asynchronous operation depends upon another object such as a form or data module, create an instance of TAsync for that object. If the global Async is used, then it's possible the asynchronous methods will be called after needed objects are freed.
procedure FormCreate(Sender : TObject); begin FAsync := TAsync.Create; FAsync.DoEvery(10, procedure begin Shape1.Left := Random(Width-Shape1.Width); end ); end; procedure FormDestroy(Sender : TObject); begin FAsync.Free; end;
Lock-Free generic collection classes for high performance use in mutlithreaded applications
- TQueue<T> - high performance queue collection that is thread safe without locking
- TStack<T> - high performance stack collection that is thread safe without locking
- THash<T> - high performance hash collection that is thread safe without locking
A background job runner which easily executes tasks in the background
Delphi's System.Threading unit includes a Task execution library which on it's surface is very useful. It suffers from a throttling algorithm that can cause performance issues for some workloads. Jobs is an attempt to execute background tasks in a highly performant way through natural throttling based on workload and system capacity. By default a global Jobs runner is created with one running thread per logical processor on the system.
// Queue a background job that will execute as soon as an idle job runner notices it. TJobManager.Excute( procedure begin // Add something to the Jobs Queue and execute as soon as possible end ); // Jobs can also return values. Asking for the return value // will cause the thread to wait until the value is available. jobCount := TJobManager.Execute<Integer>( function : Integer begin Result := 0; for i := 1 to 30 do begin sleep(1000) inc(Result); end end ); // ... do some other work here in the main thread or in other jobs Result := jobCount.Result; // will stop execution of this thread here until // the jobCount job has returned it's result. // NOTE: if you do this in the main thread, // gui operations will block at this point until // the job returns. Also, if using in the main thread // do not do operations from within the job that execute // through synchronization as it will cause a deadlock. // Sometimes you want to do a bunch of jobs and wait for them all to complete. // To do this you can use a TJobQueue queue := TJobQueue.Create; TJobManager.Execute( procedure begin // do something important end, queue ); TJobManager.Execute( procedure begin // do something else here end, queue ); queue.WaitForAll; // Execution will stop here until all of the jobs in the queue have completed.
Depending upon workload, it may be desirable to create a separate jobs queue for a particular workload. To do so call CreateJobs() with the number of threads you wish to dedicate to running the jobs.
procedure FormCreate(Sender : TObject); begin // Create a Jobs Runner with 25 threads // Note that there is a default job runner under a global variable "Jobs" // which should suffice in most applications. FJobs := TJobManager.CreateJobs(25); end; procedure Button1Click(Sender : TObject); begin FJobs.Queue( procedure begin // Add some workload to the jobs runner end ); end;
- Fork and PR
- Fixes to the core library are welcome and encouraged
- Performance improvement PRs must be provable
- Additions to the library will be accepted if it furthers the project goal to simplify asynchronous and multithreaded development and to do so with a focus on high performance.
Copyright 2017 by Bugfish Limited
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.