2 Attachment(s)
twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Class
This project is showcasing the WinRT ThreadPool class demonstrating how to create multiple threads to perform lengthy tasks and then communicate the results via "event-like" callbacks. In this example, we are using 5 threads and each thread is checking whether a rather large number is prime or not.
Be careful when checking very large numbers. If they are composite then the result will be shown very fast but if you hit a large prime (larger than what you see in the screenshot) then the algorithm will take a VERY long time to finish. Suggestions for improvement are welcome in this area! :D
Attachment 195389
Here is the demo project: Attachment 195390
Requirements: Windows 10 or later!
Re: twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Cla
Re: twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Cla
I'm always impressed by how you're advancing the WinRT topic. :thumb: I simply couldn't get the ThreadPool class to work in VB6 without a type library. :o
Re: twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Cla
A TypeLib, while incredibly useful, doesn't help at all with this. In fact, before making the WinRT TypeLib I was using your classic method of implementing delegates in a BAS module with a vtable and all. While implementing QueryInterface to respond to IID_IUnknown and the IID of the particular delegate I was trying to implement, I noticed it was also being bombarded with queries for many other IIDs so I was curious what they were all about: INoMarshal, IMarshal, IStdIdentity and a bunch of others I couldn't find any information about.
One of them in particular stood out, IAgileObject. This one is a marker interface with no methods but if you respond with S_OK to its query then your delegate object can be called freely from other threads without first creating a proxy and marshaling the call back to your single-threaded apartment (which would block the UI thread for the duration and defeat the whole purpose of multi-threading).
Of course this would promptly crash VB6 in most cases since the runtime is not initialized in new threads. The Trick has shown a way to initialize the VB6 runtime in another thread but as usual his work is way too advanced to follow without blindly copying it. On the other hand, TwinBasic is free-threaded and doesn't rely on a runtime so this method of marking delegate objects as agile works great as demonstrated in the simple example above (you'll notice the cWorkItem class implements IAgileObject).
There are some limited use cases in VB6 as well, even without initializing the runtime, for example your delegate object can call API functions provided they are declared in a TypeLib. One example would be calling SetEvent to signal an event object which would be paired with MsgWaitForMultipleObjects from your main thread. This simulates the Await behavior in .NET when your UI remains responsive while waiting for an AsyncOperation to complete. The reason SetEvent needs to reside in a TypeLib is because otherwise VB6 will try to set Err.LastDLLError and this will crash because the runtime won't be initialized so there wouldn't be an Err object.
Re: twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Cla
I didn't realize that the IAgileObject interface plays an important role. I should have.
* Class Windows.System.Threading.ThreadPool
*
* Introduced to Windows.Foundation.UniversalApiContract in version 1.0
*
* RuntimeClass contains static methods.
* Static Methods exist on the Windows.System.Threading.IThreadPoolStatics interface starting with version 1.0 of the Windows.Foundation.UniversalApiContract API contract
*
* Class Threading Model: Both Single and Multi Threaded Apartment
*
* Class Marshaling Behavior: Agile - Class is agile
Anyway, I missed it. :o
Re: twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Cla
Yeah, IAgileObject is very convenient to use but it's not the only solution for this. You could also use CoCreateFreeThreadedMarshaler on your delegate object and then in your QueryInterface implementation when you receive a query for IID_IMarshal you just return FreeThreadedMarshaler->QueryInterface(IID_IMarshal, ppv). Confirmed this also works fine but probably incurs a slight performance penalty compared to IAgileObject.
Re: twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Cla
In tB, here's a handy way to monitor objects for QueryInterface requests that aren't implemented:
Code:
[InterfaceId("D97ED766-D090-45C7-900C-C4A7B7612B09")]
Private Interface IUnsupportedInterface Extends stdole.IUnknown
Sub UnsupportedInterfaceRequested(ByRef iid As GUID, ByRef out As stdole.IUnknown)
End Interface
Add this interface to your class using `Implements IUnsupportedInterface` as the final Implements statement, and then `UnsupportedInterfaceRequested` will be triggered when any request comes in that isn't implemented.
Re: twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Cla
Another alternative is to activate trace mode in the project settings. You'll find an option for tracing `QueryInterface` results. Then review the generated log file for E_NOINTERFACE results.
Re: twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Cla
Quote:
Originally Posted by
WaynePhillipsEA
In tB, here's a handy way to monitor objects for QueryInterface requests that aren't implemented:
Code:
[InterfaceId("D97ED766-D090-45C7-900C-C4A7B7612B09")]
Private Interface IUnsupportedInterface Extends stdole.IUnknown
Sub UnsupportedInterfaceRequested(ByRef iid As GUID, ByRef out As stdole.IUnknown)
End Interface
Add this interface to your class using `Implements IUnsupportedInterface` as the final Implements statement, and then `UnsupportedInterfaceRequested` will be triggered when any request comes in that isn't implemented.
That's a handy diagnostic tool indeed but it would've been great if you could return something as well. Suppose for most GUIDs you wanted to return E_NOINTERFACE but for some S_OK would've been a useful choice too.
Re: twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Cla
You can; just set the out param to the exact interface (you may need to use one of the mem copy functions to avoid an implicit cast to the identity IUnknown interface when setting the out argument, being sure to increase the ref count of course, e.g. vbaObjSetAddref).
Re: twinBASIC - Simple MultiThreading Prime Numbers Checker with WinRT ThreadPool Cla
Code:
Set Handler = Me
PutMemPtr VarPtr(out), ObjPtr(Handler): PutMemPtr VarPtr(Handler), vbNullPtr ' This works!
vbaObjSetAddref out, ObjPtr(Handler) ' This doesn't work
Probably vbaObjSetAddref messes up the mojo with an implicit cast to IUnknown like you said. Either way this is a step up from the barbaric VB6 solution with a BAS module and a makeshift VTable! :bigyello: :thumb: