MultiThreadingDemo

Bruce Armstrong
CODE AUTHOR
Posts: 65
 1 week 22 hours ago -  1 week 22 hours ago #719 by Bruce Armstrong
Bruce Armstrong created the code: 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/PostMessage back 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=1 in DBParm (supported by some drivers) and use the RetrieveRow event. Long retrieves then don't freeze the UI.
  • Timer plus Yield() chunking: do the work in slices in a Timer event and call Yield() 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_SCHEDULER on Oracle, with PB polling for completion.

What to avoid

  • Calling CreateThread from 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.