MultiThreadingDemo
The PowerBuilder VM is not thread-safe. If any thread PowerBuilder didn't create touches a PowerBuilder object, sooner or later you get random crashes. That includes callbacks, events, and calling functions on a PB object from inside a .NET Task or a Win32 thread. So the useful patterns all do the same thing: the work runs somewhere else, and the results come back through a channel that PowerBuilder reads on its own main thread.
1. .NET assemblies plus PostMessage (my first choice)
Since PB 2019 R3 you can call .NET assemblies directly, using the .NET DLL Importer or DotNetAssembly/DotNetObject. In C# you have Task, Parallel.ForEach, Channel<T>, cancellation tokens and so on.
- PB calls something like
StartJob(jobId, Handle(this)), which returns immediately. - C# does the work on the thread pool.
- When it finishes, C# calls Win32
PostMessage(hwnd, WM_USER + n, jobId, 0). - PB handles that message in a user event mapped to
pbm_custom01, which runs on the UI thread. That event then calls C# to fetch the results, e.g.GetResult(jobId)returning JSON or a DataTable serialized to a string.
public class Worker {
[DllImport("user32.dll")] static extern bool PostMessage(IntPtr h, int msg, IntPtr w, IntPtr l);
static ConcurrentDictionary<int,string> results = new();
public void StartJob(int jobId, long hwnd) {
Task.Run(() => {
results[jobId] = DoExpensiveWork(); // never touch PB here
PostMessage((IntPtr)hwnd, 0x0400, (IntPtr)jobId, IntPtr.Zero); // WM_USER = pbm_custom01
});
}
public string GetResult(int jobId) => results.TryRemove(jobId, out var r) ? r : "";
}
This gives you real parallelism, a UI that stays responsive, progress reporting (post progress messages the same way) and cancellation. The PB side remains strictly single-threaded.
2. Separate processes
Launch worker PB executables (or any executable) with Run() or the CreateProcess API, passing a job ID on the command line. They communicate through one of:
- a database queue or status table (simple and transactional, and easy to monitor)
- files or a watched folder
- named pipes, or
WM_COPYDATA/PostMessageback to the main window's handle
This is very robust: a crash in a worker can't take down the main app, and each process has its own database connection. It suits batch jobs, report generation, imports and exports.
3. Move the work to a service
Put the heavy work behind a REST API, for example a C# Web API, PowerServer, or a Windows service polling a queue table. The PB client submits a job and later polls for its status or picks up the result. This scales beyond one machine and is where Appeon's own direction (PowerServer, C# migration) is heading anyway.
4. Lighter-weight options when you only need responsiveness
- Asynchronous DataWindow retrieval: set
Async=1in DBParm (supported by some drivers) and use theRetrieveRowevent. Long retrieves then don't freeze the UI. - Timer plus
Yield()chunking: do the work in slices in a Timer event and callYield()in loops. This isn't real concurrency, but it's often enough to keep the UI alive and allow a Cancel button. - Database-side work: stored procedures started as SQL Agent jobs, or with
DBMS_SCHEDULERon Oracle, with PB polling for completion.
What to avoid
- Calling
CreateThreadfrom PowerScript, or running PB functions as thread procedures through external function tricks. - Letting .NET or COM code raise events into, or call back into, PB objects from background threads.
- Sharing one transaction object across threads or processes. Each worker needs its own connection.
Rule of thumb
| Need | Approach |
|---|---|
| Responsive UI while doing CPU or I/O work in the same app | .NET + PostMessage |
| Long or batch jobs where isolation matters | Separate processes + queue table |
| Scalable or shared processing | REST service |
| Just a slow retrieve | Async DataWindow retrieval |
A full working example of the .NET + PostMessage pattern, with a C# class library, a PowerBuilder 2025 R2 solution, and progress and cancel handling, is attached, and is also available at github.com/bruce-armstrong/MultiThreadingDemo.
This message has an attachment file.
Please log in or register to see it.
Last Edit: 1 week 22 hours ago by Bruce Armstrong .
Please Log in or Create an account to join the conversation.