cb199911db_f.asp.htm

来自「C++builder学习资料C++builder」· HTM 代码 · 共 701 行 · 第 1/3 页

HTM
701
字号


<HTML>

  <HEAD>



<TITLE>Multithreading</TITLE>

    

  </HEAD>

  <BODY>

    

<TABLE border=0 width="100%" cellpadding=0 cellspacing=0>

<TR valign=top>

<TD width="100%">





<p class=ColumnTitle><font size="2">CBuilder 

Tech</font></p> 

 

<p class=ColumnSubtitle><font size="2">Multithreading 

/ Database Issues</font></p> 

 

<p class=BodyText> &nbsp; </p> 

 

<p class=Byline><font size="2">By Dennis P.  

Butler</font></p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=StoryTitle><font size="2"><b>Multithreading</b></font></p> 

 

<p class=StorySubtitle><font size="2"><b>Part  

II: Multithreaded Queries</b></font></p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=BodyText> We've  

seen how threads work in general in C++Builder (in Part I of this two-part  

article series). In this article's sample program, we'll be running queries in  

our threads. The program demonstrates how multithreaded queries work by letting  

the user input a query, and launch it as a thread. The results are shown in a  

separate window so the user can launch several queries and see how long it takes  

each to finish. Users can set the priority of each new query when it's created.  

The main screen keeps a running total of all queries that have completed. The  

query that's typed into the main screen will execute when the Execute Query button is clicked. </p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=BodyText> To  

create our new sample program, we must first create a main form. We'll put a <i  

style='mso-bidi-font-style:normal'>TComboBox</i> on our form, which we'll fill  

with all of our BDE aliases. By doing this, we're allowing users to select the  

database alias they wish to query. A <i>TMemo</i>  

component is used for users to enter the SQL SELECT statement to be executed.  

We're using a <i>TTrackBar</i> component,  

just as we used in the previous sample program to control the priority of the  

query thread we're creating. A <i>TBitBtn</i>  

component is used to create our new custom thread and execute it. We're also  

using several <i>TLabel</i> components to  

display the results of the query threads we've created. The form we've created  

is shown in Figure 1. </p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=Captions><img width=333 height=361  

 src="images/cb199911db_f_image002.jpg" tppabs="http://www.cbuilderzine.com/features/1999/11/cb199911db_f/cb199911db_f_image002.jpg" align=left> <br clear=all>  

<b>Figure 1:</b> The threaded query main form. </p>  

  

<p class=BodyText> &nbsp; </p>  

  

  

  

<p class=BodyText> The Execute Query button creates a new child form that runs the query and displays  

the results. There's also a spin edit control to launch batches of the same  

query simultaneously. The database and SQL statement to be executed are passed  

as arguments to this child form's <i>Create</i>  

statement. </p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=BodyText> Because  

we're using the <i>TQuery</i> component, we  

must meet some new requirements that weren't necessary in the first example -  

requirements that apply to any <i>TDataSet</i> component, such as the <i  

style='mso-bidi-font-style:normal'>TTable</i>.</p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=BodyText> The  

first requirement of a multithreaded query is that the query must be contained  

within its own BDE session. This is done by using a separate <i  

style='mso-bidi-font-style:normal'>TSession</i> component, the component that  

controls the interaction with any database. Because a default <i  

style='mso-bidi-font-style:normal'>TSession</i> component is automatically  

created in all C++Builder projects, single-threaded applications don't require  

additional database sessions. In this example, however, we're going to perform  

several independent database tasks simultaneously, so we need to create a  

separate database session for each. </p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=BodyText> To  

connect our multithreaded queries with their appropriate <i>TSession</i> components, we'll first create a new <i>TSession</i> component for our multithreaded query, then assign the <i  

style='mso-bidi-font-style:normal'>SessionName</i> property of the query to the  

name of our newly created <i>TSession</i>  

component. In the following example, we explicitly create a <i  

style='mso-bidi-font-style:normal'>TSession</i> object to demonstrate what  

needs to be set up for the component. It would also have been possible to drop  

this component on the form at design time and set up some of the properties  

then. In addition to using a separate <i>TSession</i>  

component for each <i>TQuery</i> component  

to be used in a thread, you must also use a separate <i>TDatabase</i> component and attach it to the new <i>TSession</i> via the <i>SessionName</i>  

property of the <i>TDatabase</i>.</p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=BodyText> The  

second requirement for a multithreaded query is that the threaded <i  

style='mso-bidi-font-style:normal'>TQuery</i> component must not be connected  

to a <i>TDataSource</i> in the context of  

the thread in which it will be executed. This must be done in the context of  

the primary thread. </p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=BodyText> Now that  

we have explained the additional requirements of multithreaded database  

applications, we can get back to our sample application. The multithreading  

action of this sample application takes place in the <i>frmResults</i> form. This form consists of a <i>TDBGrid</i> to display the query answer set, a <i>TLabel</i> to show messages to the user, and a button to close the  

query (see Figure 2). </p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=Captions><img width=332 height=233  

 src="images/cb199911db_f_image003.gif" align=left> <br clear=all>  

<b>Figure 2:</b> The result form for the query. </p>  

  

<p class=BodyText> &nbsp; </p>  

  

  

  

<p class=BodyText> The  

execution of the SQL query that was passed to this form is done by creating a  

new thread on the <i>Create</i> method of  

the form itself. This makes it easy to launch many different queries. By  

passing the SQL statements and database to a new instance of a <i  

style='mso-bidi-font-style:normal'>TfrmResults</i> form, a new thread will be  

created and executed. </p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=BodyText> To  

terminate the thread, we'll attach code to the <i>FormClose</i> event of the form. We do this so the thread will be  

terminated whether or not it has finished executing. To terminate the thread,  

we call its <i>Terminate</i> method. Note  

that the only purpose of calling <i>Terminate</i>  

is to set the <i>Terminated</i> property of  

the thread to <b>true</b>. This by itself doesn't affect the thread. The <i  

style='mso-bidi-font-style:normal'>Execute</i> method of the thread must have  

logic that checks to see if the thread is terminated, and exit the execution of  

the thread if it is, in fact, terminated. </p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=BodyText> First we  

create a new class, <i>TQueryThread</i>,  

which is derived from the <i>TThread</i>  

class. Each thread will have several private fields. The thread will need a <i  

style='mso-bidi-font-style:normal'>TSession</i> so it has its own handle to the  

database, a <i>TQuery</i> that will do most  

of the work, a <i>TDataSource</i> to hook  

the <i>TQuery</i> to a <i>TDBGrid</i>, and a private reference to the form that created the  

thread so it can connect the form's <i>TDBGrid</i>  

to the thread's <i>TDataSource</i>. We also  

need to override the <i>Execute</i>, <i>Constructor</i>,  

and <i>Destructor</i> methods so we can add our own code. The class declaration  

for <i>TQueryThread</i> is shown in Figure  

3. </p>  

  

<p class=BodyText> &nbsp; </p>  

  

<p class=Code><span class=Code><b>class</b>  

TQueryThread: <b>public</b> TThread</span></p>  

  

<p class=Code><span class=Code>{</span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;<b> private</b>:</span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;TSession *FSession;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <i><span Class=CodeBlue>// Our Unique Database Session. </span></i></span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;TQuery *FQuery;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <i><span Class=CodeBlue>// The Results Query. </span></i></span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;TDatabase *FDatabase&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i> <span Class=CodeBlue>// The Database Component. </span></i></span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;TDataSource *FDataSource; <i  

style='mso-bidi-font-style:normal'><span Class=CodeBlue>// Our Query  

Datasource. </span></i></span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;TForm *FOwnerForm;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <i><span Class=CodeBlue>// The Query Results form itself. </span></i></span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;AnsiString FErrorMsg;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <i><span Class=CodeBlue>// Error message variable. </span></i></span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;<b> protected</b>  

: </span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;<b> void</b>  

<b>__fastcall</b> Execute(); <i  

style='mso-bidi-font-style:normal'><span Class=CodeBlue>// Override default  

Execute Method. </span></i></span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;<b> public</b>  

: </span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;<b> virtual</b>  

<b>__fastcall</b> TQueryThread(TForm  

*AOwner, </span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TStrings *SQLStrings, AnsiString  

DatabaseName, </span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b> int</b>  

intPriority);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <i  

style='mso-bidi-font-style:normal'><span Class=CodeBlue>// Custom Create  

method. </span></i></span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;<b> __fastcall</b>  

~TQueryThread();</span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;<b> void</b>  

<b>__fastcall</b> DisconDataSource();&nbsp;&nbsp;<i> <span Class=CodeBlue>// Disconnect datasource. </span></i></span></p>  

  

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;<b> void</b>  

<b>__fastcall</b> ConnectDataSource(); <i  

style='mso-bidi-font-style:normal'><span Class=CodeBlue>// Connect the  

datasource. </span></i></span></p>  

  

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?