On https://stackoverrun.com/de/q/11468851 there is a good example from Remy Lebeau for using foreign TIdContext in a TIdTCPServer.OnExecute procedure. But this only works until Indy 9. With Indy 10 a new Thread mechanism was introduced. TIdContext no longer is directly connected to a connection. How is it now possible to create a new thread for example to disconnect the connection or write some data to the client?
On https://stackoverrun.com/de/q/11468851 there is a good example from Remy Lebeau for using foreign TIdContext in a TIdTCPServer.OnExecute procedure.
That post is just a mirror of this question here on StackOverflow:
TIdTCPServer accessing custom AContext properties
But this only works until Indy 9.
Not true. That example was written for Indy 10.
With Indy 10 a new Thread mechanism was introduced. TIdContext no longer is directly connected to a connection.
TIdContext was actually introduced in Indy 10, to separate user data from threads. The idea being that a per-client context could be allowed to move between multiple threads/fibers 1. However, TIdContext does have a Yarn property, which can be type-casted to TIdYarnOfThread to access the underlying TIdThreadWithTask that the TIdContext is currently running in.
1: but in reality, that was never implemented for threads, and fiber support didn't work out so it was abandoned.
Indy 9 and earlier used TIdPeerThread to track user data directly in threads. TIdPeerThread doesn't exist in Indy 10, it was replaced with TIdThreadWithTask and TIdContext (which is derived from TIdTask).
How is it now possible to create a new thread for example to disconnect the connection or write some data to the client?
If you want to create a custom thread class for TIdTCPServer in Indy 10, rather than (or maybe in addition to) a custom context class, then you can do the following:
derive a custom class from TIdThreadWithTask and override its virtual methods as needed.
explicitly assign a TIdSchedulerOfThread... component (TIdSchedulerOfThreadDefault or TIdSchedulerOfThreadPool) to the TIdTCPServer.Scheduler property. If you don't do this, TIdTCPServer will create an internal TIdSchedulerOfThreadDefault for itself, and you won't have an opportunity to tell it what kind of thread class to use.
assign your thread class type to the Scheduler's ThreadClass property.
This is equivalent to deriving from TIdPeerThread and setting TIdTCPServer.ThreadClass in Indy 9.
Related
Using Delphi 7 & UIB, I'm running database operations in a background thread to eliminate problems like:
Timeout
Priority
Immediate Force-reconnect after network-loss
Non-blocked UI
Keeping an opened DB connection alive
User canceling
I've read ALL related topics here, and realized: using while isMyThreadStillRuning and not UserCanceled do sleep(100); end; isn't the recommended way to do this, but rather using TEvent.WaitFor(3000)....
The solutions here are either about sending signals FROM or TO... the thread, or doing it with messages, but never both ways.
Reading the help file, I've also found TSimpleEvent, which seems to be easier to use.
So what is the recommended way to communicate between Main-UI + DB-Thread in both ways?
Should I simply create 2+2 TSimpleEvent?
to start a new transaction (thread should stop sleeping)
force-STOP execution
to signal back if it's moved to a new stage (transaction started / executed / commited=done)
to signal back if there is any error happened
or should there be only 1 TEvent?
Update 2:
First tests show:
2x TSimpleEvent is enough (1 for Thread + 1 for Gui)
Both created as public properties of the background thread
Force-terminating the thread does not work. (Too many errors impossible to handle..)
Better to set a variable like (Stop_yourself) and let it cancel and free itself, (while creating a new instance from the same class and try again.)
(still work in progress...)
You should move the query to a TThread. Unfortunately, anonymous threads are not available in D7 so you need to write your own TThread derived class. Inside, you need its own DB connection to prevent shared resources. From the caller method, you can wait for the thread to end. The results should be stored somewhere in the caller class. Ensure that the access to parameters of the query and for storing the result of the query is handled thread-safe by using a TMutex or TMonitor.
I have a unit with an initialization and finalization section. This unit contains a complex object which is instantiated in the initialization and destroyed in the finalization. However, this object also contains an ADO Connection. That makes it an issue when using this across threads, because ADO is COM, and needs to be initialized for every thread.
This is how I currently handle this global object instance:
uses
ActiveX;
...
initialization
CoInitialize(nil);
_MyObject:= TMyObject.Create;
finalization
_MyObject.Free;
CoUninitialize;
end.
This only works on the main thread. Any other thread wouldn't be able to access it, and will return an exception CoInitialize has not been called.
How do I get around this to make this unit thread-safe? I would need a way to hook every creation/destruction of any thread created, and each thread would need to refer to a different instance of this object. But how to go about doing so?
Well, as you already say yourself, each thread needs to call CoInitialize separately. And in addition, each thread needs to have its own ADOConnection too.
I think you need to leave the idea of using the single global object/connection from that unit. Just repeat that object creation and destruction in each thread. When the thread types are different, then you could design a base thread class on top of them. If the object is too big (has overhead with regard to the thread) or does not 'fit' completely in the thread, then split the object design.
For now, your question sounds like just wanting to keep convenience, but if it is really necessary to centralize the ADO connection involvement, then maybe you could implement multi-cast events for the connection events of both main thread and the other threads. Logging in should not be a problem for successive connections: just store the login values and feed them to the threads.
While another design might be a better solution, you can declare _MyObject as threadvar to have a separate instance for each thread. In addition you can move the CoInitialize/CoUnitialize into the constructor/destructor of TMyObject.
I cannot give advice on when to create and free these instances as I have no idea how your threads are created and freed.
As we know, NHibernate sessions are not thread safe. But we have a code path split in several long running threads, all using objects loaded in the initial thread.
using (var session = factory.OpenSession())
{
var parent = session.Get<T>(parentId);
DoSthWithParent(session, parent);
foreach (var child in parent.children)
{
parallelThreadMethodLongRunning.BeginInvoke(session, child);
//[Thread #1] DoSthWithChild(child #1) -> SaveOrUpdate(child #1) + Flush()
//[Thread #2] DoSthWithChild(child #2) -> SaveOrUpdate(child #2) + Flush()
//[Thread #3] DoSthWithChild(child #3) -> SaveOrUpdate(child #3) + Flush()
// -> etc... changes to be persisted immediately, not all at the end.
EndInvoke();
}
DoFinalChangesOnParentAndChildren(parent);
session.Flush();
}
}
One way would be a session for each thread, but that would require the parent object to be reloaded in each. Plus, the final method is also doing changes on the children and would run in a StaleObjectException if another session changed it meanwhile, or had to be evicted/reloaded.
So all threads have to use the same session. What is the best way to do this?
Use save queue in initial thread (thread safe implementation), which is polled in a loop (instead of EndInvoke()) from the main thread. Child threads can insert NHibernate objects to be saved by the main thread.
Use some callback mechanism to save/flush objects in main thread. Is there something similar possible to UI thread callback in WPF, Control.Invoke() or BackgroundWorker?
Put Save/Flush accesses into lock(session) blocks? Maybe dangerous, because modifying the NHibernate objects might change the session, even if not doing a Save()/Flush().
Or should I live with the database overhead to load the same objects for separate sessions in each thread, evict and reload them in the main thread and then do changes again? [edit: bad "solution" due to object concurrency/risk of stale objects]
Consider also that the application has a business logic layer above NHibernate, which has similar objects, but sends it's property values to the NHibernate objects on it's own Save() command, only then modifying them and doing NHibernate Save()/Flush() immediately.
Edit:
It's important that any read operation on NHibernate objects may change the session - lazy loading, chilren collection change under certain conditions. So it is really better to have a business object layer on top, which synchronizes all access to NHibernate objects. Considering the database operations take only a minimum time of the threads (mainly occasional status settings), and most is for calculations, watching, web service access and similar, the performance loss by data layer synchronization is negligible.
Firstly, if I understand correctly, different threads may be updating the same objects. In that case, nHibernate or not, you're performing several updates on the same objects concurrently, which may lead to unexpected results.
You may want to tweak your design a bit to ensure that an object can be only updated by (at most) a single thread.
Now, assuming your flow may include having the same threads reading the same data (but writing different data), I'd suggest using different sessions- one per thread, and utilizing 2nd level cache;
2nd level cache is kept at the SessionFactory (rather than in the Session) level, and is therefore shared by all session instances.
The session object is not thread safe, you can't use it over different threads. The SaveOrUpdate in your sepperate threads will most likely crash your program or corrupt your database. However what about creating the data set you want to update and do the SaveOrUpdate actions in your main thread (were your session is created)?
You should observe the following practices when creating NHibernate
Sessions: • Never create more than one concurrent ISession or
ITransaction instance per database connection.
• Be extremely careful when creating more than one ISession per
database per transaction. The ISession itself keeps track of updates
made to loaded objects, so a different ISession might see stale data.
• The ISession is not threadsafe! Never access the same ISession in
two concurrent threads. An ISession is usually only a single
unit-of-work!
I am developing an UI application that creates a COM object along the way.
The problem is, I want to "move" this COM object entirely on a different thread.
What I do is this:
create the new thread I want to move the object into (with CreateThread API)
after entering this thread, I'm calling PeekMessage to setup a message queue for it
calling CoInitialize, CoCreateInstance to create the COM object, QueryInterface to get the interface I want
finally I call a method on the interface that displays a MessageBox with the value returned by GetCurrentThreadId() (I have access to the VB6 code of the COM library within which the object resides).
The problem is, as this message box shows, the object methods are still executed on the original UI thread, not on the thread I created and done all those steps into. One more thing to mention, after calling the interface method, I'm also setting up a classic message loop in it.
How can I change this behaviour and achieve what I want? (that is, I want the COM object calls that originate from my newly created thread to be executed ON IT, not on the original application thread)
Here's some pseudocode to make it even more clearer:
void myMainUIMethod(){
MessageBox(GetCurrentThreadId()); // displays 1
CreateThread(&myCOMObjectThreadProc);
}
void myCOMObjectThreadProc(){
MessageBox(GetCurrentThreadId()); // displays 2
CoInitialize(NULL);
myObject = CoCreateInstance(myObjectsCLSID);
myObjectInterface = myObject->QueryInterface(myObjectInterfaceCLSID);
myObjectInterface->showThreadIDMessageBox(); // this would be the COM object method call
}
And, in the VB6 code of the object, here's the pseudo-definition of showThreadIDMessageBox.
Public Sub showThreadIDMessageBox()
Call MessageBox(GetCurrentThreadId()) //displays 1, I want it to display 2
End Sub
I have achieved what I wanted by CoUninitalizing on the main thread, before creating the new thread. But why does this happen? If COM was initialized ON THE MAIN THREAD before I'm creating the new thread, maybe for some reason it had to be..I would't want the application to crash later because I had to call CoUninitialize before creating my new thread. Here's some pseudocode that illustrates that whichever thread calls CoInitialize first will be the one picked by the STA objects.
void myMainUIMethod(){
MessageBox(GetCurrentThreadId()); // displays 1
CoUninitialize(); // uninitialize COM on the main thread
CreateThread(&myCOMObjectThreadProc);
***i: MessageBox("When you want to initialize COM on main thread, confirm this");
CoInitialize();
}
void myCOMObjectThreadProc(){
MessageBox(GetCurrentThreadId()); // displays 2
***ii: MessageBox("When you want to initialize COM on the new thread, confirm this");
CoInitialize(NULL);
myObject = CoCreateInstance(myObjectsCLSID);
myObjectInterface = myObject->QueryInterface(myObjectInterfaceCLSID);
myObjectInterface->showThreadIDMessageBox(); // this shows 2 IF ***ii is confirmed before ***i, 1 otherwise
}
Thank you very much in advance,
Corneliu
Looks like your problem is that your COM component threading model is not specified in registry key InprocServer32. This means that object is considered as STA (single-threaded apartment) but will be loaded to main (or host) STA, not the STA that created it. This is the first thread that called CoInitialize. To be created in same STA that called CoCreateInstance you must create HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{Your CLSID}\InprocServer32#ThreadingModel registry value and set it to Apartment.
Quote from MSDN (InprocServer32 registry key documentation):
If ThreadingModel is not present or is not set to a value, the server is loaded into the first apartment that was initialized in the process. This apartment is sometimes referred to as the main single-threaded apartment (STA). If the first STA in a process is initialized by COM, rather than by an explicit call to CoInitialize or CoInitializeEx, it is called the host STA. For example, COM creates a host STA if an in-process server to be loaded requires an STA but there is currently no STA in the process.
I have finally achieved what I wanted! Adding a CoUninitialize call in the main UI thread, before creating the new thread has solved it. This happens because STA COM objects will be handled on the thread that first calls CoInitialize. Now all the calls to the objects methods are reported to be executed on the thread I created and the main window of the object (the COM component has a Form) is reported to belong to it too! (used WinSpy++ to test that).
There is still a question (and a problem) though..why does it behave this way?
Everywhere I search on the internet I see answers telling that a STA COM component will be fully executed on the thread it is created on (provided that CoInitialize or CoInitializeEx with COINIT_APARTMENTTHREADED had been called before), no matter what. Why does it matter if I called CoInitialize on another thread before..that's just plain stupid in my opinion for Microsoft to do so :), plus it might damage the future behaviour of my application, as I stated before.
EDIT: The correct answer is the one posted by Frost. Thank you again.
The threads are running in parallel and that's what they are meant to do. you need to synchronize between the two threads if you want one object to wait for some operation on other thread to complete. Event object will serve for your purpose.
You need to choose Free Threading as the Threading Model of the COM class when creating it. With C++ ATL, this is an option in the wizard when you select New -> COM class (or something like it). In .NET languages, I think this is specified as an attribute in the class.
BTW, you don't need to call QueryInterface after CoCreateInstance (unless you need more than one interface pointer). Just pass the GUID of the interface you want as the 4th parameter to CoCreateInstance.
Ah, I think I might know the problem now: it sounds like the VB6 COM object you are creating was registered as single-threaded, not apartment-threaded; this means that the object gets created on whichever thread is your app is the first to call CoInitialize().
This explain the behavior you are seeing: if you let your main thread CoInitialize() first, it becomes the "main thread" as far as COM is concerned, so the CoCreate ends up creating the object on it, even though it's CoCreated on a different thread. (This is only the case for single-threaded objects.)
But when you let your other thread CoInitialize() first, it is the "main thread" for COM, so the object gets created where you want it.
Can you change the threading model of your VB object to apartment instead of single? This would enable it to get created on the thread that calls CoCreate().
The problem is, I cannot change the threading model of the VB6 component since it is already used in other applications and it might damage it's behaviour.
...looks like that won't work for you. I guess you can check what the current threading model is, and if you can confirm that it's single, then you'll have an explanation for why it behaves the way it does, which might help you work with it.
--
So why does COM behave that way? - A: legacy compat issues. The Single Thread model is a holdover from before windows had threads in the first place, when every process had just one thread, and code didn't have to make any assumptions about synchronizations between objects within a process. To preserve this illusion and allow objects that were written assuming single-threaded COM to be used in a multithreaded environment, COM introduced the 'single' model, also known as 'legacy STA'. More details on this page, scroll down or search for "Legacy STA" for the details. COM basically puts all of these 'single' objects on the same [STA] thread - and uses whichever thread just happens to be the first to call CoInitialize. When you CoUninit and CoInit again on another thread, you're essentially restarting COM; so it's now the second thread that is the new "first thread to call CoInit", so that's why COM then ends up using that one...
(Legacy STA is such an old issue is was actually hard to track down any details; nearly all other articles mention apartment, free and both options; but there's rarely details about 'single'.)
Normally when using Delphi/Lazarus threads it is possible to change the priority of the thread so that it can execute faster.
That said, is it possible to do the same for the Indy10 TCP Server component? If so, how? Is it even advisable to do so?
thanks
TIdTCPServer uses normal Delphi/Lazarus threads.
If you are running on Windows, you can call the Win32 API GetCurrentThread() and SetThreadPriority() functions. Or, you can set the priority of a TThread object using Indy's SetThreadPriority() (Indy 9 and earlier) or IndySetThreadPriority() (Indy 10) function.
If you are using a modern Delphi version, you can use the TThread.CurrentThread property to get a TExternalThread object representing the calling thread. TExternalThread is a TThread descendant, so it can be passed to IndySetThreadPriority(), or you can just set the TThread.Priority property directly.
If you want to access Indy's own TThread objects directly, it can be done, depending on which version of Indy you are using:
If you are using Indy 9 and earlier, the server events provide a TIdPeerThread object pointer. TIdPeerThread is a TThread descendant. Simple enough.
If you are using Indy 10, on the other hand, it goes out of its way to hide/abstract the threads away from you. The server events now provide a TIdContext object pointer, and TIdContext is not the thread itself. But all is not lost. If the TIdTCPServer.Scheduler property is pointing at a TIdSchedulerOfThread-derived component (TIdTCPServer uses TIdSchedulerOfThreadDefault internally if you do not provide a Scheduler), then you can type-cast the TIdContext.Yarn property to a TIdYarnOfThread object pointer and then access the TIdYarnOfthread.Thread property, which is a TIdThreadWithTask object pointer. TIdThreadWithTask is a TThread descendant.