This is pretty easy to do with AsyncCalls. Suppose your non-stream code looks like this (ignoring all errors):
Query := 'select (*) as DBCount from...'; ExecuteSQL(SqlConnection,Query); SqlResult := GetSqlResult(SqlConnection); SqlRow := GetSqlRow(SqlResult); MyLabel.Text := SqlRow[0]; ...Go on to do other things...
If the second line is blocked (waiting for a server response). Your new code will look like this:
uses AsyncCalls; //added to your existing uses statement ... procedure DoesSomething(); var Thread: TAsyncCall; //your interface to AsyncCalls procedure AsyncSqlCall(); //this is a LOCAL procedure Query := 'select (*) as DBCount from...'; ExecuteSQL(SqlConnection,Query); SqlResult := GetSqlResult(SqlConnection); SqlRow := GetSqlRow(SqlResult); EnterMainThread; try Assert(GetCurrentThreadId = MainThreadId); MyLabel.Text := SqlRow[0]; finally LeaveMainThread; end; begin //this begins proc DoSomething() ... Thread := LocalAsyncCall(@AsyncSqlCall); ...Go on to do other things... end;
All we did is put the blocking SQL calls in the local proc and tell AsyncCalls to execute it on another thread while the main thread continues to execute. The only tricky part is to use a VCL that is not thread safe. So I used this line in the main thread.
If at some point you need to make sure that the Async thread is completed, you must run this line to block the main thread before AsyncSqlCall completes:
Thread.sync;
It is very nice here that AsyncCalls handles everything related to creating a thread pool, creating threads, etc. Although not shown in this example, you can pass variables to your stream and return a value. You do not need to use local proc, but this gives it access to all local vars. You can make it all global and then run the Async thread in one routine and test its completion in another.
Limitations:
Your Async thread should not touch (read or write) anything but its own variables, and your main thread should not touch them while the Async thread is running. You must encode it that way. Nothing will stop you from creating complete chaos. In the above example, the main thread should not touch queries, SqlConnection, SqlResult, and SqlRow. If any part of your code used one of these vars before calling Thread.sync, your code would work - but make exceptions in strange places that you never expected. So keep it simple.
Your Async thread should not use VCL. The above example shows one of several ways to safely overcome this limitation.
Finally:
AsyncCalls is not a complete multithreaded structure. This is just a way to call processes and functions asynchronously (i.e., without waiting). Do not try to push it too far - I mean, do not try to make it the basis for a fully multitasking program.