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

本文探讨了在多线程环境中高效处理大量图像数据并将其存入数据库的方法。通过为每个线程分配独立的数据库连接,解决了并发访问数据库时的性能瓶颈问题。详细介绍了两种避免连接超时的有效策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、问题介绍

项目需要实时获取并处理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;
}

三、上述代码存在的问题

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

这里写图片描述

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

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

方案1:

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值