参考<<Proc*C/C++ Precompiler Programmer's Guide>> Release 8.1.6 ---Multithreaded Applications一章的例子写了个简单的多线程程序,书中例子似乎是针对linux环境的,需要pthread.h或thread.h两个头文件,但我电脑上的linux环境没有安装oracle,于是就仿照书中例子用VC做工具写了个简单的多线程的例子(windows多线程函数似乎和linux的多线程函数用法差别挺大),其实还有不少不懂的地方,而且现在也感受不到效率和并发问题。代码如下:
/*
* Name: proc_mul.pc
* 预编译条件: mode=ORACLE parse=FULL
* 利用多线程,更新表中记录;每个线程拥有单独的上下文环境
* 表结构: create table accounts (account number(36), balance(36,2))
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlca.h>
#include <windows.h>
#define CONNINFO "test/test"
#define THREADS 3
DWORD WINAPI do_transaction(); // 处理交易
void get_transaction(); // 获得记录
void logon();
void logoff();
void err_report(); // 错误处理
/*
* 记录线程id和运行时上下文的结构
*/
struct parameters
{
sql_context *ctx;
int thread_id;
};
typedef struct parameters parameters;
struct record_log
{
char action; // 操作类型
unsigned int from_account; // 出账号
unsigned int to_account; // 入账号
float amount; // 转账金额
};
typedef struct record_log record_log;
/**待更新数据记录**/
record_log records[] = {
{'M',10001,10008,10},
{'M',10002,10008,10},
{'M',10003,10008,10},
{'M',10004,10008,10},
{'M',10005,10008,10},
{'M',10006,10008,10},
{'M',10007,10008,10},
{'M',10001,10008,10},
{'M',10002,10008,10}
};
static unsigned int trx_nr = 0;
static unsigned int flag_over = 0;
HANDLE hMutex; // 定义互斥对象
/********主函数*************/
void main()
{
sql_context ctx[THREADS];
HANDLE thread_id[THREADS];
// int status;
parameters params[THREADS];
int i;
/*********支持多线程*********/
EXEC SQL ENABLE THREADS;
EXEC SQL WHENEVER SQLERROR DO err_report(sqlca);
/*create THEAD sessions by connect THREAD times*/
for(i = 0; i < THREADS; i++)
{
printf("start session %d.../n", i);
EXEC SQL CONTEXT ALLOCATE :ctx[i]; // 分配并初始化一块用于指向一个新的运行时上下文的内存
logon(ctx[i], CONNINFO);
}
/*创建互斥对象*/
hMutex = CreateMutex(NULL, FALSE, "trx_nr");
if(hMutex)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
printf("only one instance can run...");
return;
}
}
/*创建线程*/
for(i = 0; i < THREADS; i++)
{
params[i].ctx = ctx[i];
params[i].thread_id = i;
thread_id[i] = CreateThread(NULL, 0, do_transaction, ¶ms[i], 0, &thread_id[i]);
CloseHandle(thread_id[i]);
}
/******利用一个全局变量判断所有线程是否结束********/
while(flag_over != THREADS)
{
Sleep(10000);
}
/***************有问题待查***************
//关闭线程及数据库连接
for(i = 0; i < THREADS; i++)
{
if(WaitForSingleObject(thread_id[i],0)==WAIT_OBJECT_0)
{
printf("test %d terminated/n", i);
printf("stop session %d.../n", i);
logoff(ctx[i]);
EXEC SQL CONTEXT FREE :ctx[i];
}
}
**************************************/
}
/*
* 数据处理函数,一次处理recors[]中的一条记录
* recors[]记录通过get_transaction()函数管理
*/
DWORD WINAPI do_transaction(parameters *params)
{
struct sqlca sqlca; // 局部通讯区
record_log *trx;
sql_context ctx = params->ctx;
/*处理数据,循环,知道records[]中数据被全部处理完*/
while(trx_nr < sizeof(records)/sizeof(record_log))
{
WaitForSingleObject(hMutex,INFINITE);
get_transaction(&trx); // 得到一条记录,需要用指针的地址
printf("thread %d is executing transaction.../n", params->thread_id);
ReleaseMutex(hMutex);
EXEC SQL WHENEVER SQLERROR DO err_report(sqlca);
EXEC SQL CONTEXT USE :ctx; // 每个线程具有独立的上下文环境
switch(trx->action)
{
case 'M':
EXEC SQL UPDATE ACCOUNT SET BALANCE=BALANCE+:trx->amount WHERE ACCOUNT=:trx->to_account;
EXEC SQL UPDATE ACCOUNT SET BALANCE=BALANCE-:trx->amount WHERE ACCOUNT=:trx->from_account;
break;
default:
break;
}
EXEC SQL COMMIT; // 事务提交
Sleep(2000);
}
/*线程结束,关闭数据库连接*/
printf("transaction finished, thread %d terminated/n", params->thread_id);
printf("stop session %d.../n", params->thread_id);
logoff(ctx);
EXEC SQL CONTEXT FREE :ctx; // 释放内存
flag_over++;
return 0;
}
/*
* 管理recors[]记录,控制并发
*/
void get_transaction(record_log **trx)
{
*trx = &records[trx_nr]; // 获得当前要处理的记录
trx_nr++;
}
/*
* 连接数据库
*/
void logon(sql_context ctx, char *connect_info)
{
EXEC SQL WHENEVER SQLERROR DO err_report(sqlca);
EXEC SQL CONTEXT USE :ctx;
EXEC SQL CONNECT :connect_info;
printf("connect.../n");
}
/*
* 断开数据库连接
*/
void logoff(sql_context ctx)
{
EXEC SQL WHENEVER SQLERROR DO err_report(sqlca);
EXEC SQL CONTEXT USE :ctx;
EXEC SQL COMMIT WORK RELEASE;
printf("logged off/n");
}
/*
* 错误处理函数
*/
void err_report(struct sqlca sqlca)
{
if(sqlca.sqlcode < 0)
{
printf("/n%.*s/n/n", sqlca.sqlerrm.sqlerrml,sqlca.sqlerrm.sqlerrmc);
exit(1);
}
}
在VC6.0环境下调试没问题,执行结果也正常。不过控制台的输出比较奇怪,有时候乱七八糟的,似乎在执行printf函数的时候出现了写问题,查了很久也不知道为什么。