pro*c初学2---简单多线程例子

来源:互联网 发布:2018 专升本 网络教育 编辑:程序博客网 时间:2024/05/16 05:04

    参考<<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, &params[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函数的时候出现了写问题,查了很久也不知道为什么。