[ Pobierz całość w formacie PDF ]
.You do not need to use the main VCL thread toaccess TFont, TPen, TBrush, TBitmap, TMetafile, or TIcon.Canvas objects can beused outside the Synchronize method by locking them (see Locking objects onpage 7-6)." While list objects are not thread-safe, you can use a thread-safe version,TThreadList, instead of TList.Using thread-local variablesYour Execute method and any of the routines it calls have their own local variables,just like any other C++ routines.These routines also can access any global variables.In fact, global variables provide a powerful mechanism for communicating betweenthreads.Sometimes, however, you may want to use variables that are global to all the routinesrunning in your thread, but not shared with other instances of the same thread class.You can do this by declaring thread-local variables.Make a variable thread-local byadding the __thread modifier to the variable declaration.For example,int __thread x;declares an integer type variable that is private to each thread in the application, butglobal within each thread.The __thread modifier can only be used for global (file-scope) and static variables.Pointer and Function variables can t be thread variables.Types that use copy-on-write semantics, such as AnsiStrings don t work as thread variables either.Aprogram element that requires runtime initialization or runtime finalization cannotbe declared to be a __thread type.The following declarations require runtime initialization and are therefore illegal.int f( );int __thread x = f( ); // illegalInstantiation of a class with a user-defined constructor or destructor requires runtimeinitialization and is therefore illegal:class X {X( );~X( );};X __thread myclass; // illegalChecking for termination by other threadsYour thread begins running when the Execute method is called (see Executingthread objects on page 7-10) and continues until Execute finishes.This reflects themodel that the thread performs a specific task, and then stops when it is finished.Sometimes, however, an application needs a thread to execute until some externalcriterion is satisfied.You can allow other threads to signal that it is time for your thread to finishexecuting by checking the Terminated property.When another thread tries toterminate your thread, it calls the Terminate method.Terminate sets your thread sWr i t i ng mul t i - t hr eaded appl i c at i ons 7-5Co o r d i n a t i n g t h r e a d sTerminated property to true.It is up to your Execute method to implement theTerminate method by checking and responding to the Terminated property.Thefollowing example shows one way to do this:void __fastcall TMyThread::Execute(){while (!Terminated)PerformSomeTask();}Writing clean-up codeYou can centralize the code that cleans up when your thread finishes executing.Justbefore a thread shuts down, an OnTerminate event occurs.Put any clean-up code inthe OnTerminate event handler to ensure that it is always executed, no matter whatexecution path the Execute method follows.The OnTerminate event handler is not run as part of your thread.Instead, it is run inthe context of the main VCL thread of your application.This has two implications:" You can t use any thread-local variables in an OnTerminate event handler (unlessyou want the main VCL thread values)." You can safely access any components and VCL objects from the OnTerminateevent handler without worrying about clashing with other threads.For more information about the main VCL thread, see Using the main VCL threadon page 7-4.Coordinating threadsWhen writing the code that runs when your thread is executed, you must considerthe behavior of other threads that may be executing simultaneously.In particular,care must be taken to avoid two threads trying to use the same global object orvariable at the same time.In addition, the code in one thread can depend on theresults of tasks performed by other threads.Avoiding simultaneous accessTo avoid clashing with other threads when accessing global objects or variables, youmay need to block the execution of other threads until your thread code has finishedan operation.Be careful not to block other execution threads unnecessarily.Doing socan cause performance to degrade seriously and negate most of the advantages ofusing multiple threads.Locking objectsSome objects have built-in locking that prevents the execution of other threads fromusing that object instance.7-6 Dev el oper s Gui deCo o r d i n a t i n g t h r e a d sFor example, canvas objects (TCanvas and descendants) have a Lock method thatprevents other threads from accessing the canvas until the Unlock method is called.The VCL also includes a thread-safe list object, TThreadList.CallingTThreadList::LockList returns the list object while also blocking other executionthreads from using the list until the UnlockList method is called.Calls toTCanvas::Lock or TThreadList::LockList can be safely nested.The lock is not releaseduntil the last locking call is matched with a corresponding unlock call in the samethread.Using critical sectionsIf objects do not provide built-in locking, you can use a critical section.Criticalsections work like gates that allow only a single thread to enter at a time.To use acritical section, create a global instance of TCriticalSection.TCriticalSection has twomethods, Acquire (which blocks other threads from executing the section) and Release(which removes the block).Each critical section is associated with the global memory you want to protect.Everythread that accesses that global memory should first use the Acquire method toensure that no other thread is using it.When finished, threads call the Release methodso that other threads can access the global memory by calling Acquire.Warning Critical sections only work if every thread uses them to access the associated globalmemory.Threads that ignore the critical section and access the global memorywithout calling Acquire can introduce problems of simultaneous access.For example, consider an application that has a global critical section variable,pLockXY, that blocks access to global variables X and Y.Any thread that uses X or Ymust surround that use with calls to the critical section such as the following:pLockXY->Acquire(); // lock out other threadstry{Y = sin(X);}__finally{pLockXY->Release();}Using the multi-read exclusive-write synchronizerWhen you use critical sections to protect global memory, only one thread can use thememory at a time.This can be more protection than you need, especially if you havean object or variable that must be read often but to which you very seldom write.There is no danger in multiple threads reading the same memory simultaneously, aslong as no thread is writing to it.When you have some global memory that is read often, but to which threadsoccasionally write, you can protect it using TMultiReadExclusiveWriteSynchronizer.Thisobject acts like a critical section, but one which allows multiple threads to read thememory it protects as long as no thread is writing to it.Threads must have exclusiveaccess to write to memory protected by TMultiReadExclusiveWriteSynchronizer
[ Pobierz całość w formacie PDF ]