使用BCB陆续十多年了,因为一直在工控领域主要是做串口和网络通信类的小软件,BCB6.0足够用,对C++应用得并不是很深。
在读写外部设备时也会用多线程(为了避免主程序在操控过程中的卡顿),如串口和数据库,最主要还是操作数据库,开几个线程来分开独立读写数据库还是挺自在的。
但因为自己的项目中几乎没有出现同时写一个表的情况,所以都是在主程序中控制多线程的写入数据库时机,多少年来一直这么干也没出过事。
新到一个工控公司,出于成本的考虑需要用一些轻量型的数据库,于是就不得不改换用新的数据库,这个数据库没有了并发机制,读写不能同时执行。这下惯用的多线程程序接二连三地崩溃,因此不得不认真研究多线程的同步问题,用用心,很快就改好了,按照自己的方案新改一个小程序备案。
网上绝大多数C++多线程案例都是正确的废话,理论性的,顶多用个控制台来演示下自加自减效果,对于菜鸟级的是没法移植,我就从实用性的角度写一个简单理解和便于移植的BCB6.0的多线程以供入门者参考。
简单说明下多线程的原理,不复制理论了,自己的理解:因为电脑的CPU执行速度很快,一般它90%以上的时间都是空闲的,软件执行正常的内存操作都是很快的,而读写外部设备是较慢的,很花时间(相对读写内存)。如果用单线程就会出现程序卡顿的现象,如果非必须的情况:如程序中的每一步环环相扣,上一步的结果是下一步的执行输入条件,那也没法用多线程。
本多线程是使用的临界区——CRITICAL_SECTION
模拟状态是这样的:
线程正在写一个文件时主程序通知该线程再写入一条数据,在两次写文件都还没完成时主线程又要通知该线程执行读文件。
在此不考虑优先级问题,只要按照线程通知的顺序执行就行了。
程序的执行原理是这样的,一个多线程里面包含了读和写test.txt记事本的功能。
可通过临界区成功解决了线程执行的冲突问题,最终3个事件都会完美执行。可以很轻易地改成对数据库的操作,若使用ADO需要在线程中对COM对象初始化(CoInitialize)。
本例是解决同一个多线程的同时执行冲突问题,同理也可以用全局变量临界区来协调多个不同线程的同步问题。
2、多线程消息机制
3、读写记事本
4、线程与VCL控件同步
在读写外部设备时也会用多线程(为了避免主程序在操控过程中的卡顿),如串口和数据库,最主要还是操作数据库,开几个线程来分开独立读写数据库还是挺自在的。
但因为自己的项目中几乎没有出现同时写一个表的情况,所以都是在主程序中控制多线程的写入数据库时机,多少年来一直这么干也没出过事。
新到一个工控公司,出于成本的考虑需要用一些轻量型的数据库,于是就不得不改换用新的数据库,这个数据库没有了并发机制,读写不能同时执行。这下惯用的多线程程序接二连三地崩溃,因此不得不认真研究多线程的同步问题,用用心,很快就改好了,按照自己的方案新改一个小程序备案。
网上绝大多数C++多线程案例都是正确的废话,理论性的,顶多用个控制台来演示下自加自减效果,对于菜鸟级的是没法移植,我就从实用性的角度写一个简单理解和便于移植的BCB6.0的多线程以供入门者参考。
简单说明下多线程的原理,不复制理论了,自己的理解:因为电脑的CPU执行速度很快,一般它90%以上的时间都是空闲的,软件执行正常的内存操作都是很快的,而读写外部设备是较慢的,很花时间(相对读写内存)。如果用单线程就会出现程序卡顿的现象,如果非必须的情况:如程序中的每一步环环相扣,上一步的结果是下一步的执行输入条件,那也没法用多线程。
本多线程是使用的临界区——CRITICAL_SECTION
模拟状态是这样的:
线程正在写一个文件时主程序通知该线程再写入一条数据,在两次写文件都还没完成时主线程又要通知该线程执行读文件。
在此不考虑优先级问题,只要按照线程通知的顺序执行就行了。
程序的执行原理是这样的,一个多线程里面包含了读和写test.txt记事本的功能。
因为读写记事本的消耗时间很短,在写记事本里面故意加了2s延时以造成多线程的明显冲突。
可通过临界区成功解决了线程执行的冲突问题,最终3个事件都会完美执行。可以很轻易地改成对数据库的操作,若使用ADO需要在线程中对COM对象初始化(CoInitialize)。
本例是解决同一个多线程的同时执行冲突问题,同理也可以用全局变量临界区来协调多个不同线程的同步问题。
在大当量的任务中用这个方案是有弊端的,非本文考虑范围。
核心代码:
void __fastcall Wrtext::Execute()
{
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)) //一直等主线程消息通知
{
try
{
Lock(); //进入临界区 如果还没出临界区又有新消息进来,不用担心,它们会排队等待的
try
{
switch(msg.message)
{
case WRITE_DATA : //写记录
WriteData(msg.wParam, msg.lParam);
Sleep(2000);//故意延时仿真执行事件的时间长 //
break;
case READ_DATA : //读记录
Synchronize(ReadData);
break;
default
:
break;
}
}
catch(Exception &e)
{
//LOG(e); //LOG是日志记录类
}
catch(...)
{
//LOG("未知异常!");
}
}
__finally
{
Unlock(); //退出临界区 让排队的可以进来了
}
}
}
对于新手可以学习以下几个知识点:
1、多线程临界区同步2、多线程消息机制
3、读写记事本
4、线程与VCL控件同步
5、控件自适应窗体大小变化(鼠标拉动界面边界改变大小看看效果)