One common thing you may want to do with thread pools is to dispatch several tasks to the pool, do some other work, and then wait for the result. Another twist is that you want to continue working in the UI and then at some point in the future, a method is called when the set of tasks are completed. I've been adding some code to the SyncObjs.pas unit, and specifically the
THandleObject
class. I've added a class function called WaitForMultiple()
. This allows you to pass in an array of THandleObject class instances on which to wait. You can wait for one or all. This is just a wrapper around WaitForMultipleObjectsEx
. In the thread pool class I've added a parameter to the QueueXXWorkItem methods allowing you to optionally pass in a TEvent
object that will be signaled once the work item completes. Since you may want to dispatch many work items, all of which must be completed before continuing, you'd call THandleObject.WaitForMultiple()
or a new class method on the TThreadPool
class.An interesting thing to note is that if the UI thread blocks waiting for the work items, it seems that the items are not dispatched into the worker threads at all and the application hangs. There is nothing in the MSDN documentation for QueueUserWorkItem. If I perform the wait on a separate wait thread, it seems to work OK. Any ideas? Are the tasks dispatched onto the worker threads via synchronization with the message queue? I would have figured that there was a control thread that waits for things to exist in the work item queue and then does the dispatching.
Thanks a lot Allen for your posts from Kona. They were very, very instructive. Once again, it was proved, imho, that a community driven development is the path to follow. Now I remember a post in .non-tech newsgroup (with title “Top 3 requests” IIRC), in which someone asked for thread-safe VCL (ie. a very daunting task, imho). Nick responded “We’re very interested in that” – response which triggered an avalanche of posts from which the conclusion was that isn’t needed to have a thread-safe VCL but rather a easy, flexible way of sync/async signaling/messaging/passing data between threads and only the main thread to have the UI message loop, ie. no forms, panels, group boxes aso. in 2ndary threads, even it would be nice. IOW, a very “doable” task, even in Tiburon time frame. It was an impressive example of how community reacts, simplifies, and gives the right focus which is needed today for any IT company, imho.
ReplyDeleteOn the technical side of things, “where is the code?” (TM) Can you post the new source in CC?
...while waiting for your code, some more things to note:
ReplyDelete- Another multithreading scenario it would be a ThreadedDataModule. Yes, I know that nowadays we can create & use a TDataModule in a thread, but what I mean is to have a TDataModule assigned to a thread in a Delphi manner, ie. Bind the UI elements (“data aware controls”) to the data sources from the thread, connect to database, fetch the data, call the methods of the threaded datasets and the UI will respond etc. All these things would happen at different moments in time, at programmer's will. (ie. something like
TMainForm.OnButtonClick(Sender);
begin
with MyThreadedDataModule.SQLTable1 do
begin
SQL.Text:='SELECT * FROM T1 WHERE '+cMyNewFilter;
Open; //here the UI will be updated acordingly
end;
end;
- Do you are aware of very interesting things at
http://www.bluebytesoftware.com/blog/CategoryView,category,Technology.aspx
especially at
http://www.bluebytesoftware.com/blog/2007/04/19/MichaelSuesssParallelProgrammingInterviews.aspx
and, of course, at
http://www.thinkingparallel.com/
hth
Your code will eventually call QueueWorkItem(Sender, WorkerEvent, WT_EXECUTEDEFAULT) and the WT_EXECUTEDEFAULT flag tells QueueWorkItem and hence QueueUserWorkItem will execute PaintLines and hence PaintLine in a non IO thread.
ReplyDeleteIf too many non IO threads attempt to update the screen while an IO thread is updating a screen, a deadlock will occur.
Besides, non IO threads are not supposed to update the screen.
Anyway, what is eventually painted on the screen comes from a data structure.
ReplyDeleteSo, any access to a data structure must be serialized, and the serialization of the data structure for the screen occurs only in an IO thread.
If disorderly access to the screen's data structure occurs, what happens is usually data corruption. The OS might not react kindly to that.
Chee Wee,
ReplyDeleteThe deadlock is not in the PaintLines call. It is perfectly fine to paint to the screen from a background thread.
Allen.
Okay...
ReplyDeleteIn that case, isn't locking the canvas unnecessary?
I don't see any SendMessage call in your code, directly or indirectly.
ReplyDeleteIs the exact code at: http://cc.codegear.com/Download.aspx?ID=25023 what you're talking about?
Now that I've sit myself down, examining the code by running it, instead of calculating the issues through my head, I see where your SendMessage call is. It's in the code within FForm.ListBox1.Items.Add.
ReplyDeleteIf SendMessage blocks, your app will be in a deadlock, isn't it?
Chee Wee,
ReplyDeleteThat's it. I was trying to highlight that making a "thread-safe" VCL is not something easily done nor desirable in all cases.
Allen.
Calling QueueIOWorkItem appears to resolve your issue. I've experimented several times, and found no deadlocks.
ReplyDeleteQueueWorkItem, performed the same number of times, seems to run into deadlocks more often.
These tests are performed while within the IDE and stepping through the program, and may not be conclusive though.
Chee Wee,
ReplyDeleteSo tell me *why* calling QueueIOWorkItem resolves it? I have my doubts that that actually resolves the problem.
Allen.
My goodness! you've been busy Allen!
ReplyDeleteI'll have to take a moment to catch up on all your posts since September.
Coincidentally I have recently written a threading library for our projects here. I have an inter-process and inter-thread waitable queue, message posting and synchronous waits (e.g. a synch. wait where ProcessMessages is called when in the main thread, or a thread's message queue is pumped while in a thread). I am not using the APC functions yet - was thinking of looking into that.
ReplyDelete