VC+ADO+多线程高效、安全的读写数据库

本文介绍了一种在多线程环境下,通过为每个线程建立独立数据库连接的方法,实现40路相机图像数据的实时处理与入库。讨论了数据库连接超时问题,并提供了两种解决方案。

一、问题介绍

项目需要实时获取并处理40路相机的现场图像,并将处理结果写入到数据库,采用的方案是使用多线程技术,创建40个工作者线程,每个线程建立一个数据库连接。本文仅将项目中遇到的问题以及解决方法做些记录。

二、多线程连接数据库

在单线程程序中,只需建立一个数据库连接。在多线程中,因为多线程是并行处理的(对于多核CPU来说),若按单线程方式只建立一个数据库连接,多线程共用此连接,那么必然存在排队等待的问题。比较好的方法是每个线程建立一个单独的连接。

  1. 采用ADO技术在多线程中建立多个数据库连接时,必须在每个线程中使用CoInitialize(NULL)初始化COM库。
  2. 线程结束时,必须在每个线程中使用CoUninitialize()释放COM资源。

三、实现代码

下面代码展示了如何建立多线程并在线程中建立数据库连接的过程。数据库为:SQL Server 2012,编译工具为VS2010,ADO技术。

/////// main.cpp //////////
//////////////////////////

#include "iostream"
#include "atlstr.h"
using namespace std;
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")

unsigned int __stdcall threadProc(PVOID);//工作者线程函数

int total = 40;  //工作者线程总数
int index = 0;   
void main()
{
    ::CoInitialize(NULL);//初始化COM库
    for ( int i = 0; i < total; i++)
    {
        _beginthreadex(NULL,0,threadProc,NULL,0,NULL);
    }
    getchar();  // 主线程阻塞在此处

}

//工作者线程函数
unsigned int __stdcall threadProc(PVOID)
{
    int threadId = index;
    index++;

    ::CoInitialize(NULL);//初始化COM库

    _ConnectionPtr pConn;
    pConn.CreateInstance("ADODB.Connection");
    pConn->ConnectionTimeout = 30;  //设置连接超时时间 30s
    try
    {
        pConn->Open("Driver={SQL Server};Server=LENOVO-PC01;Database=dbTest;UID=sa;PWD=mima","","",adConnectUnspecified);
    }
    catch(_com_error e)
    {
        ::MessageBox(NULL,e.Description(),_T("警告"),MB_OKCANCEL);
    }
    for (int j = 0 ; j < 100; j++)
    {
        CString strSql;
        strSql.Format(_T("insert into Table3(id,age) values( %d ,%d)"),ThreadId,j);

        _variant_t recordset;
        pConn->Execute((_bstr_t)strSql,&recordset,adCmdText);
    }
    if (pConn->State == adStateOpen )
    {
        pConn->Close();
    }
    ::CoUninitialize();//反初始化COM库
    return 0;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

三、上述代码存在的问题

当线程数一多,使用上述代码百分百会出现问题,提示如下: 

这里写图片描述

原因在于数据库建立连接是个非常消耗资源与时间的工作,同时创建大量连接,完成这些连接的耗时必将非常长,而数据库默认的连接超时为30s,一旦超过这个时间,程序就会传回超时错误。解决方法如下:

  1. 将连接超时调大,大到足够完成所有连接的创建。
  2. 连接排队创建,只有上个连接创建完成,才开始创建下一个连接,直至所有连接创建完成。

方案1:

 //设置超时时间足够大,此处为3000s
 pConn->ConnectionTimeout = 3000;  //设置连接超时时间 3000s
 
  • 1
  • 2
  • 1
  • 2

方案2,推荐采用,实现代码如下:

/////// main.cpp //////////
//////////////////////////
#include "iostream"
#include "atlstr.h"
using namespace std;
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")

unsigned int __stdcall threadProc(PVOID);//工作者线程函数

int total = 40;//工作者线程总数
int index = 0;
int *flag = new int[total](); // 连接创建成功标志

void main()
{
    ::CoInitialize(NULL);//初始化COM库

    HANDLE *hThread = new HANDLE[total];

    for ( int i = 0; i < total; i++)
    {
        hThread[i] = (HANDLE)_beginthreadex(NULL,0,threadProc,NULL,CREATE_SUSPENDED,NULL);
    }
    int f = flag[4];

    ResumeThread(hThread[0]);
    for (int i = 1; i < total ; i++)
    {
        while(flag[i-1] != 1)
        {
            //空转
        }
        ResumeThread(hThread[i]);
    }

    getchar();

    delete [] hThread;
    delete [] flag;
}

unsigned int __stdcall threadProc(PVOID)
{
    int Threadid = index;

    ::CoInitialize(NULL);//初始化COM库

    _ConnectionPtr pConn;
    pConn.CreateInstance("ADODB.Connection");
    pConn->ConnectionTimeout = 30;
    try
    {
        pConn->Open("Driver={SQL Server};Server=LENOVO-PC01;Database=huayuTest;UID=sa;PWD=MIMA","","",adConnectUnspecified);
    }
    catch(_com_error e)
    {
        ::MessageBox(NULL,e.Description(),_T("!!!!"),MB_OKCANCEL);
    }

    cout << "Connect:" <<index<<" has been established"<<endl;
    flag[index] = 1;  // 
    index++;

    for (int j = 0 ; j < 100; j++)
    {
        CString strSql;
        strSql.Format(_T("insert into Table3(id,age) values( %d ,%d)"),Threadid,j);

        _variant_t recordset;
        pConn->Execute((_bstr_t)strSql,&recordset,adCmdText);
    }
    if (pConn->State == adStateOpen )
    {
        pConn->Close();
    }

    ::CoUninitialize();//反初始化COM库
    return 0;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

四、总结

多线程并发访问数据库,解决方法是在每个线程中建立一个数据库连接,需要注意的两点如下:

  1. 必须在每个线程中都调用CoInitialize(NULL)和CoUninitialize()来初始化COM库和释放COM库资源。
  2. 同时创建多个数据库连接时,需要注意数据库连接超时问题,解决方法是分批创建数据库连接。

转载请注明作者和出处:http://blog.youkuaiyun.com/holamirai,未经允许请勿用于商业用途。


转自:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值