マルチスレッド環境で動作するアプリケーションの例を以下に示します。
スレッドを2つ起動し、並列で動作する例です。
以下の例に示したアプリケーションを動作させるためには、コンパイル・リンク時のコマンドのオプションに-DNTを指定してください。
以下の例に示したアプリケーションを動作させるためには、コンパイル・リンク時のコマンドのオプションに-DLinuxを指定してください。
#if defined(NT)
#include <windows.h>
#else
#if defined(Linux)
#include <stdio.h>
#include <pthread.h>
#else
#include <stdio.h>
#include <thread.h>
#endif
#endif
#include "sqlrdbei.h"
#define SQL_STATEMENT1 \
"SELECT WISTRU1 FROM SI_SCHM1.SI_TABLE WHERE WIUNQU1=1" …… (1)
void* sub_threadA(void *); …… (2)
void* sub_threadB(void *);
int main(void)
{
SQLRETURN ret =0;
SQLHDBS ses_id =0;
SQLHDBS ses_id2=0;
#if defined(NT)
unsigned long t_threadA;
unsigned long t_threadB;
HANDLE thread_tbl[2];
#else
#if defined(Linux)
pthread_t t_threadA;
pthread_t t_threadB;
#else
thread_t t_threadA;
thread_t t_threadB;
thread_t ret_threadA;
thread_t ret_threadB;
#endif
#endif
ret = SQLThrAllocID( &ses_id ); …… (3)
if ( ret != SQLRDB_NORMAL ) {
printf( "SQLThrAllocID(code) = %d\n" , ret );
return 1;
}
ret = SQLThrAllocID( &ses_id2 );
if ( ret != SQLRDB_NORMAL ) {
printf( "SQLThrAllocID(code) = %d\n" , ret );
ret = SQLThrFreeID( ses_id );
return 1;
}
#if defined(NT)
t_threadA = (unsigned long)_beginthread( sub_threadA, 0, &ses_id); …… (4)
thread_tbl[0] = (HANDLE)t_threadA;
t_threadB = (unsigned long)_beginthread( sub_threadB, 0, &ses_id2);
thread_tbl[1] = (HANDLE)t_threadB;
WaitForMultipleObjects( 1, thread_tbl, TRUE, INFINITE ); …… (5)
#else
#if defined(Linux)
pthread_create(&t_threadA, NULL, sub_threadA, (void *)&ses_id ); …… (4)
pthread_create(&t_threadB, NULL, sub_threadB, (void *)&ses_id2);
pthread_join(t_threadA, NULL); …… (5)
pthread_join(t_threadB, NULL);
#else
thr_create(NULL, 0, sub_threadA, (void *)&ses_id, THR_BOUND, &t_threadA); …… (4)
thr_create(NULL, 0, sub_threadB, (void *)&ses_id2, THR_BOUND, &t_threadB);
thr_join(t_threadA, &ret_threadA, NULL); …… (5)
thr_join(t_threadB, &ret_threadB, NULL);
#endif
#endif
ret = SQLThrFreeID( ses_id ); …… (6)
ret = SQLThrFreeID( ses_id2 );
return 0;
}(1) 実行するSQL文を定義します。
(2) スレッド(サブプロセス)の関数を宣言します。
(3) セションを作成します。セションを作成する関数を実行することによって、セションIDを受け取ります。
(4) スレッドを作成して、sub_threadAおよびsub_threadBを実行します。
(5) sub_threadAおよびsub_threadBの両方の処理が終了してからセションを破棄するように制御します。
(6) セションを破棄します。
void* sub_threadA(void *ses_id_p)
{
EXEC SQL BEGIN DECLARE SECTION;
char SQLSTATE[6]; …… (1)
char SQLMSG[256];
VARCHAR statement[256];
char dataA[53];
EXEC SQL END DECLARE SECTION;
SQLHDBS ses_id = *(SQLHDBS *)ses_id_p;
SQLRETURN ret=0;
memset(SQLSTATE, 0x00, 6);
memset(SQLMSG, 0x00, 256);
memset(dataA, 0x00, 53);
ret = SQLThrStartID( ses_id ); …… (2)
if ( ret != SQLRDB_NORMAL ) {
printf( "SQLThrStartID(code) = %d\n" , ret );
return 0;
}
EXEC SQL DECLARE CU1 CURSOR FOR CMD1; …… (3)
EXEC SQL WHENEVER SQLERROR GO TO :CONNECT_ERROR;
EXEC SQL CONNECT TO DEFAULT; …… (4)
printf( "CONNECT(SQLSTATE) = %s\n" , SQLSTATE );
printf( "CONNECT(SQLMSG) = %s\n" , SQLMSG );
EXEC SQL WHENEVER SQLERROR CONTINUE;
strcpy( statement.sqlvar, SQL_STATEMENT1 );
statement.sqllen = strlen( statement.sqlvar );
EXEC SQL PREPARE CMD1 FROM :statement;
printf( "PREPARE(SQLSTATE):%s\n", SQLSTATE );
printf( "PREPARE(SQLMSG):%s\n", SQLMSG );
EXEC SQL OPEN CU1 ; …… (5)
printf( "OPEN(SQLSTATE):%s\n", SQLSTATE );
printf( "OPEN(SQLMSG):%s\n", SQLMSG );
while ( strcmp(SQLSTATE,"00000")==0 ) {
EXEC SQL FETCH CU1 INTO :dataA ; …… (6)
printf( "FETCH(DATA):%s\n", dataA );
printf( "FETCH(SQLSTATE):%s\n", SQLSTATE );
printf( "FETCH(SQLMSG):%s\n", SQLMSG );
}
EXEC SQL CLOSE CU1; …… (7)
printf( "CLOSE(SQLSTATE):%s\n", SQLSTATE );
printf( "CLOSE(SQLMSG):%s\n", SQLMSG );
EXEC SQL DEALLOCATE PREPARE CMD1;
printf( "DEALLO_PREP(SQLSTATE):%s\n", SQLSTATE );
printf( "DEALLO_PREP(SQLMSG):%s\n", SQLMSG );
EXEC SQL COMMIT WORK; …… (8)
printf( "COMMIT(SQLSTATE) = %s\n" , SQLSTATE ) ;
printf( "COMMIT(SQLMSG) = %s\n" , SQLMSG );
EXEC SQL DISCONNECT ALL; …… (9)
printf( "DISCONNECT(SQLSTATE) = %s\n" , SQLSTATE );
printf( "DISCONNECT(SQLMSG) = %s\n" , SQLMSG );
ret = SQLThrEndID( ses_id ); …… (10)
if ( ret != SQLRDB_NORMAL ) {
printf( "SQLThrEndID(code) = %d\n" , ret );
return 0;
}
return 0;
CONNECT_ERROR: …… (11)
printf( "CONNECT(SQLSTATE) = %s\n" , SQLSTATE );
printf( "CONNECT(SQLMSG) = %s\n" , SQLMSG );
ret = SQLThrEndID ( ses_id );
return 0;
}(1) SQLSTATEとSQLMSGを各スレッドで宣言します。
(2) セションを開始します。セションを開始する関数を実行することにより、セションとスレッド(sub_threadA)が対応付けられます。
(3) カーソルを宣言します。
(4) サーバとコネクションを接続します。
(5) カーソルをオープンします。
(6) カーソルを位置づけ、その行を読み込みます。
(7) カーソルをクローズします。
(8) トランザクションをCOMMIT文により終了します。
(9) サーバとのコネクションを切断します。
(10) セションを終了します。
(11) サーバとのコネクションに失敗した場合は、セションを終了します。
void* sub_threadB(void *ses_id_p)
{
EXEC SQL BEGIN DECLARE SECTION;
char SQLSTATE[6]; …… (1)
char SQLMSG[256];
EXEC SQL END DECLARE SECTION;
SQLHDBS ses_id = *(SQLHDBS *)ses_id_p;
SQLRETURN ret=0;
memset(SQLSTATE, 0x00, 6);
memset(SQLMSG, 0x00, 256);
ret = SQLThrStartID( ses_id ); …… (2)
if ( ret != SQLRDB_NORMAL ) {
printf( "SQLThrStartID(code) = %d\n" , ret );
return 0;
}
EXEC SQL WHENEVER SQLERROR GO TO :CONNECT_ERROR;
EXEC SQL CONNECT TO 'SI_DB333'; …… (3)
printf( "CONNECT(SQLSTATE) = %s\n" , SQLSTATE );
printf( "CONNECT(SQLMSG) = %s\n" , SQLMSG );
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL INSERT INTO SI_SCHM1.SI_TABLE2
VALUES(1,1,1,1,1,1,1,1,1,1,'R','DD','BBBB'); …… (4)
printf( "INSERT(SQLSTATE):%s\n", SQLSTATE );
printf( "INSERT(SQLMSG):%s\n", SQLMSG );
EXEC SQL COMMIT WORK; …… (5)
printf( "COMMIT(SQLSTATE) = %s\n" , SQLSTATE ) ;
printf( "COMMIT(SQLMSG) = %s\n" , SQLMSG );
EXEC SQL DISCONNECT ALL; …… (6)
printf( "DISCONNECT(SQLSTATE) = %s\n" , SQLSTATE );
printf( "DISCONNECT(SQLMSG) = %s\n" , SQLMSG );
ret = SQLThrEndID( ses_id ); …… (7)
if ( ret != SQLRDB_NORMAL ) {
printf( "SQLThrEndID(code) = %d\n" , ret );
return 0;
}
return 0;
CONNECT_ERROR: …… (8)
printf( "CONNECT(SQLSTATE) = %s\n" , SQLSTATE );
printf( "CONNECT(SQLMSG) = %s\n" , SQLMSG );
ret = SQLThrEndID( ses_id );
return 0;
}(1) SQLSTATEとSQLMSGを各スレッドで宣言します。
(2) セションを開始します。セションを開始する関数を実行することにより、セションとスレッド(sub_threadB)が対応付けられます。
(3) サーバとコネクションを接続します。
(4) INSERT文を実行して値を挿入します。
(5) トランザクションをCOMMIT文により終了します。
(6) サーバとのコネクションを切断します。
(7) セションを終了します。
(8) サーバとのコネクションに失敗した場合は、セションを終了します。
マルチスレッド環境で動作するアプリケーションを作成する場合の注意事項
アプリケーションを作成するには、ホスト変数および複数スレッド間の排他などを考慮して、実行したSQL文によるデータベースの検索および更新結果が正しくなるようにしなければなりません。以下に注意点を示します。
状態変数(SQLSTATE)およびメッセージ変数(SQLMSG)は、関数単位に宣言してください。
データ操作で使用するホスト変数は、auto変数を使用することを推奨します。extern変数およびstatic変数を使用する場合は、複数スレッドで共通に使用されるため、排他を考慮したアプリケーションを作成する必要があります。
1つのセションに対して複数のスレッドが存在する場合、各スレッドが同時に動作しないようにしてください。セマフォを利用するなどして排他制御を考慮し、アプリケーションを作成してください。
セションを破棄する前には、必ずDISCONNECT文を実行してください。
マルチスレッド環境でアプリケーションを実行する場合も、トランザクション単位にSymfoware/RDBで排他制御を行います。また、カーソル操作についても、カーソルをオープンした後に、単一コネクションにおける複数のスレッド間では同じカーソルを操作できますが、複数のコネクションにまたがって同じカーソルを操作することはできません。
状態変数およびメッセージ変数の宣言については、“2.7 名前の一意性と有効範囲”を参照してください。