C++Builder 程序员博客
28 Aug
我查了RASENTRY结构体定义头文件ras.h
在第1172行有#define RASEO_RequireEncryptedPw 0×00000400,查了资料这个值就是选项中数据加密的,去掉它是不是就没了加密?
我将RASENTRY的dwfOptions值只设为re.dwfOptions =RASEO_ShowDialingProgress;只显示拨号进度时,“要求数据加密”还是被选中。
郁闷,这个re.dwfOptions=?时,才能不选中这个“要求数据加密”呢?
对端(服务器)要求的,无法取消。
不是吧,我创建的L2TP,只有去掉这个选项后,才能连接成功呢。所以我才要在创建连接时去掉它啊。
肯定是可以去掉的,多试试。
不能让此贴沉下啊!我的问题还没解决呢!高手哪里去了?
注意这两个选项
#define RASEO_RequireMsEncryptedPw 0×00000800
#define RASEO_RequireDataEncryption 0×00001000
看看re.dwfOptions的值是多少,试试:
re.dwfOptions = re.dwfOptions & (~RASEO_RequireMsEncryptedPw);
或:
re.dwfOptions = re.dwfOptions & (~RASEO_RequireDataEncryption);
我这里没有环境,没法测试。
re.dwfOptions = 1024262928-16;
按CCRUN的两个值,都做了。还是不行哦!老妖,能否帮忙琢磨下呢?
我这里没有环境,没办法帮你测试。
MSDN上的资料可曾仔细看过?
http://msdn.microsoft.com/en-us/library/aa920252.aspx
我在Windows网络编程,查了option的构成。都试了。
帖子要沉下了?^_^
已经搞定
楼上的兄弟,我拨号时老是报741错误,但我手工用程序创建的拨号连接是可以正常联通的啊,还有什么要设置?
我的参数如下:
RasEntry->dwfOptions <0×210>
RasEntry->dwfOptions2 <0×4>
26 Aug
如果窗体多了的话,自动创建的话效率会不会低?
设置个Button在运行时候创建好么?
若是程序窗体超多,代码中创建,运行完删除清出内存好。
否则,让程序默认启动吧。
仅仅个人看法。
<
建议数据模块(DataModule)和主窗体自动创建。其他的子窗体按需动态创建,并根据具体的需求,关闭后隐藏或销毁。
都没有问题
老妖得建议很中肯。
看楼主需要吧,我现在在做个界面 有个工具栏 点击工具栏按钮则出现相应的界面
楼主软件比较大的话 还是建议 动态创建
<
弱弱的问一下,如果子窗体的界面比较复杂,用代码创建可行吗???
<
多谢大家了,感觉上是这样的。可以结贴了!
3 Jul
怎么使用createthread创建的线程,要等到主线程运行结束后才显示结果啊。。
我创建了一个线程,然后在主线程里使用while(i)等待子线程执行完毕,但是子线程貌似没有执行。
fff=false;
DWORD WINAPI goline (LPVOID pParam) {
cout<<"ffffff";
fff=true;
}
HANDLE d=CreateThread(NULL,0,goline,NULL,0,0);
while(!fff);
没有执行?
你代码放在哪个位置的?
贴出你具体代码
bool fff=false;
DWORD WINAPI goline (LPVOID pParam) {
ShowMessage("fffffffff");
fff=true;
}
//—————————————————————————
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//—————————————————————————
void __fastcall TForm1::Button1Click(TObject *Sender)
{
HANDLE d=CreateThread(NULL,0,goline,NULL,0,0);
while(!fff); //在这里等待子线程执行结束,貌似要一直等待下去。。。。
}
加上 Application->ProcessMessages(); 可以了。
我之前在创建窗口的时候 也加了这行代码,但是不行。
是不是要 随时用随时加?
是的。随用随加。
2 Jul
#include <windows.h>
#include <windowsx.h>
#include <ddraw.h>
#define INITGUID
BOOL InitWindow( HINSTANCE hInstance, int nCmdShow );
LRESULT CALLBACK q( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) ;
HWND hwnd ;
int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg; //消息结构
if (!InitWindow(hInstance,nCmdShow))
{
MessageBox ( GetActiveWindow(), "初始化DirectDraw过程中出错!", "Error", MB_OK ) ;
return false;
}//初始化窗体
while(1) //消息循环
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message==WM_QUIT) //退出消息循环
break;
TranslateMessage(&msg); //得到消息,处理回调函数
DispatchMessage(&msg);
}
}
return msg.wParam;
}
//*********************************************************
//函数:InitWindow( )
//功能:创建窗口
//*********************************************************
static BOOL InitWindow( HINSTANCE hInstance, int nCmdShow )
{
WNDCLASS wc;
wc.style = NULL; //窗口类风格
wc.lpfnWndProc = q; //指向窗口过程函数的指针
wc.cbClsExtra = 0; //窗口类附加数据
wc.cbWndExtra = 0; //窗口类附加数据
wc.hInstance = hInstance; //拥有窗口类的实例句柄
wc.hIcon = NULL; //最小窗口图标
wc.hCursor = NULL; //窗口内使用的光标
wc.hbrBackground = NULL; //用来着色窗口背景的刷子
wc.lpszMenuName = NULL; //指向菜单资源名的指针
wc.lpszClassName = NULL;// 指向窗口类名的指针
RegisterClass(&wc); //注册窗口
hwnd=CreateWindow("menpao_RPG_DEMO","menpao_RPG_DEMO",WS_POPUP|WS_MAXIMIZE,0,0,GetSystemMetrics( SM_CXSCREEN ),GetSystemMetrics( SM_CYSCREEN ), NULL,NULL,hInstance,NULL);
if( !hwnd )
{
MessageBox ( GetActiveWindow(), "初始化DirectDraw过程中出错!", "Error", MB_OK ) ;
return FALSE;
}
ShowWindow(hwnd,nCmdShow); //显示窗口
UpdateWindow(hwnd); //刷新窗口
return TRUE;
}
//************************************************************
//函数:WinProc( )
//功能:处理主窗口消息
//************************************************************
LRESULT CALLBACK q( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch( message )
{
case WM_KEYDOWN://击键消息
switch( wParam )
{
case VK_ESCAPE:
MessageBox ( GetActiveWindow(), "初始化DirectDraw过程中出错!", "Error", MB_OK ) ;
PostQuitMessage( 0 );//退出
break;
}
return 0;
case WM_LBUTTONDOWN:
MessageBox ( GetActiveWindow(), "初始化DirectDraw过程中出错!", "Error", MB_OK ) ;
return 0;
case WM_RBUTTONDOWN:
MessageBox(hwnd,NULL,"mouse",MB_OK);
return 0;
case WM_MBUTTONDBLCLK:
MessageBox(hwnd,NULL,"mouse",MB_OK);
return 0;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam); //调用缺省消息处理过程
}
程序执行到
if( !hwnd )
{
MessageBox ( GetActiveWindow(), "初始化DirectDraw过程中出错!", "Error", MB_OK ) ;
return FALSE;
}
这里就会报错 也就是说无法产生hwnd 为什么啊???
你还没向系统注册窗口吧
@.@没看到,你注册了,不好意思,怎么会没获得窗口句柄呢。。。。应该没问题啊。
wc.lpszClassName = NULL;// 指向窗口类名的指针
必须给出窗口类名
窗口类名与CreateWindow第一个参数相同
为什么还是用不成?改过了 还是无法创建
把报错贴上来看看
把报错贴上来看看
wc.lpszClassName 为类名,必须要有,它的值要与CreateWindow 第一个参数相同(也是类名)
clean 试试,vc++有时候就有这个毛病!
你创建一个win32默认的工程,看看人家是怎么创建的。还有就是你可以调试,用getlasterror来获取错误的值,在c++错误查看工具中看是什么原因。自己用调试啊!
13 May
小弟想把所有函数放进一个头文件里,可是有一个函数中要用到Form1->ListBox1->Items->Add(tttt);,编译出错,头文件应该怎么写呢?
你放函数的头文件上面,包含Form1的头文件就可以了
两个方法
1.在头文件里#include "Unit1.h"
2.改你的函数,传一个TList * MyList的参数,在调用的时候传Form1->ListBox1
错了,是TListBox* MyListBox
添加了#include "Unit1.h",编译通过可是在运行时出现错误:出现这种情况:这是怎么回事?
什么错误?踢出来看看。
file:///C:/Documents%20and%20Settings/韧韧/桌面/未命名.JPG
这里上传不了图片的,你直接把文字踢出来
怎么发不了图片?
一个警告提示框:debugger exception notification
access violation at address 00402e68 in module
并且Form1->ListBox1->Items->Add(tttt);这一行蓝色加深
tttt是啥东东?
你这个函数是啥时候调用的?确信Form1已经创建了吗?
tttt 就当是一个字符串吧,这个函数是在按下一个按钮后调用的
调用你这个函数前,Form1必需是已经创建了的
谢谢~
5 Apr
请问什么是多线程,如何创建多线程,还请高手给个这方面的例子.
在C++Builer中多线程的实现
还在Dos时代,人们就在寻求一种多任务的实现。于是出现了TSR类型的后台驻留程序,比较有代表性的有Side Kick、Vsafe等优秀的TSR程序,这类程序的出现和应用确实给用户使用计算机带来了极大的方便,比如Side Kick,我们编程可以在不用进编辑程序的状态下,一边编辑源程序,一边编译运行,非常方便。但是,Dos单任务操作系统的致命缺陷注定了在Dos下不可能开发出真正的多任务程序。进入Windows3.1时代,这种情况依然没有根本的改变,一次应用只能做一件事。比如数据库查询,除非应用编得很好,在查询期间整个系统将不响应用户的输入。
进入了Windows NT和Windows 9x时代,情况就有了彻底的改观,操作系统从真正意义上实现了多任务(严格地说,Win9x还算不上)。一个应用程序,在需要的时候可以有许多个执行线程,每个线程就是一个小的执行程序,操作系统自动使各个线程共享CPU资源,确保任一线程都不能使系统死锁。这样,在编程的时候,可以把费时间的任务移到后台,在前台用另一个线程接受用户的输入。对那些对实时性要求比较高的编程任务,如网络客户服务、串行通信等应用时,多线程的实现无疑大大地增强了程序的可用性和稳固性。
在Windows NT和Windows 9x中,多线程的编程实现需要调用一系列的API函数,如CreateThread、ResumeThread等,比较麻烦而且容易出错。我们使用Inprise公司的新一代RAD开发工具C++Builder,可以方便地实现多线程的编程。与老牌RAD工具Visual Basic和Delphi比,C++Builer不仅功能非常强大,而且它的编程语言是C++,对于系统开发语言是C的Windows系列操作系统,它具有其它编程语言无可比拟的优势。利用C++Builder提供的TThread对象,多线程的编程变得非常简便易用。那么,如何实现呢?且待我慢慢道来,让你体会一下多线程的强大功能。
1. 创建多线程程序:
首先,先介绍一下实现多线程的具体步骤。在C++Builder中虽然用Tthread对象说明了线程的概念,但是Tthread对象本身并不完整,需要在TThread下新建其子类,并重载Execute方法来使用线程对象。在C++Builder下可以很方便地实现这一点。
在C++Builder IDE环境下选择菜单File|New,在New栏中选中Thread Object,按OK,接下来弹出输入框,输入TThread对象子类的名字MyThread,这样C++Builder自动为你创建了一个名为TMyThread的TThread子类。同时编辑器中多了一个名为Unit2.cpp的单元,这就是我们创建的TMyThread子类的原码,如下:
#include
#pragma hdrstop
#include “Unit2.h”
#pragma package(smart_init)
//---------------------
// Important: Methods and properties of objects in VCL can only be
// used in a method called using Synchronize, for example:
//
// Synchronize(UpdateCaption);
//
// where UpdateCaption could look like:
//
// void __fastcall MyThread::UpdateCaption()
// {
// Form1-> Caption = “Updated in a thread”;
// }
//--------------------
__fastcall MyThread::MyThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//--------------------
void __fastcall MyThread::Execute()
{
//---- Place thread code here ----
}
//---------------------
其中的Execute()函数就是我们要在线程中实现的任务的代码所在处。在原代码中包含Unit2.cpp,这个由我们创建的TMyThread对象就可以使用了。使用时,动态创建一个TMyThread 对象,在构造函数中使用Resume()方法,那么程序中就增加了一个新的我们自己定义的线程TMyThread,具体执行的代码就是Execute()方法重载的代码。要加载更多的线程,没关系,只要继续创建需要数量的TMyThread 对象就成。
以上我们初步地实现了在程序中创建一个自定义的线程,并使程序实现了多线程应用。但是,多线程应用的实现,并不是一件简单的工作,还需要考虑很多使多个线程能在系统中共存、互不影响的因素。比如,程序中公共变量的访问、资源的分配,如果处理不当,不仅线程会死锁陷入混乱,甚至可能会造成系统崩溃。总的来讲,在多线程编程中要注意共享对象和数据的处理,不能忽视。因此,下面我们要讲的就是多线程中常见问题:
2. 多线程中VCL对象的使用
我们都知道,C++Builder编程是建立在VCL类库的基础上的。在程序中经常需要访问VCL对象的属性和方法。不幸的是,VCL类库并不保证其中对象的属性和方法是线程访问安全的(Thread_safe),访问VCL对象的属性或调用其方法可能会访问到不被别的线程所保护的内存区域而产生错误。因此,TThread对象提供了一个Synchronize方法,当需要在线程中访问VCL对象属性或调用方法时,通过Synchronize方法来访问属性或调用方法就能避免冲突,使各个线程之间协调而不会产生意外的错误。如下所示:
void __fastcall TMyThread::PushTheButton(void)
{
Button1-> Click();
}
void __fastcall TMyThread::Execute()
{
…
Synchronize((TThreadMethod)PushTheButton);
…
}
对Button1-〉Click()方法的调用就是通过Synchronize()方法来实现的,它可以自动避免发生多线程访问冲突。在C++Builder中,虽然有一些VCL对象也是线程访问安全的(如TFont、TPen、TBrush等),可以不用Sychronize()方法对它们的属性方法进行访问调用以提高程序性能,但是,对于更多的无法确定的VCL对象,还是强烈建议使用Synchronize()方法确保程序的可靠性。
3. 多线程中公共数据的使用
程序设计中难免要在多个线程中共享数据或者对象。为了避免在多线程中因为同时访问了公共数据块而造成灾难性的后果,我们需要对公共数据块进行保护,直到一个线程对它的访问结束为止。这可以通过临界区域(Critical Section)的使用来实现,所幸的是在C++Builder中,给我们提供了一个TCriticalSection对象来进行临界区域的划定。该对象有两个方法,Acquire()和Release()。它设定的临界区域可以保证一次只有一个线程对该区域进行访问。如下例所示:
class MyThread : public TThread
{
…
private:
TCriticalSection pLockX;
int x;
float y;
…
};
void __fastcall MyThread::Execute()
{
…
pLockX-> Acquire();//Here pLockX is a Global CriticalSection variable.
x++;
y=sin(x);
pLockX-> Release();
…
}
这样,对公共变量x,y的访问就通过全局TCriticalSection 对象保护起来,避免了多个线程同时访问的冲突。
4. 多线程间的同步
当程序中多个线程同时运行,难免要遇到使用同一系统资源,或者一个线程的运行要依赖另一个线程的完成等等,这样需要在线程间进行同步的问题。由于线程同时运行,无法从程序本身来决定运行的先后快慢,使得线程的同步看起来很难实现。所幸的是Windows系统是多任务操作系统,系统内核为我们提供了事件(Event)、Mutex、信号灯(semaphore)和计时器4种对象来控制线程间的同步。在C++Builder中,为我们提供了用于创建Event的TEvent 对象供我们使用。
当程序中一个线程的运行要等待一项特定的操作的完成而不是等待一个特定的线程完成时,我们就可以很方便地用TEvent对象来实现这个目标。首先创建一个全局的TEvent对象作为所有线程可监测的标志。当一个线程完成某项特定的操作时,调用TEvent对象的SetEvent()方法,这样将设置这个标志,其他的线程可以通过监测这个标志获知操作的完成。相反,要取消这个标志,可以调用ResetEvent()方法。在需要等待操作完成的线程中使用WaitFor()方法,将一直等待这个标志被设置为止。注意WaitFor()方法的参数是等待标志设置的时间,一般用INFINITE表示无限等待事件的发生,如果其它线程运行有误,很容易使这个线程死住(等待一个永不发生的事件)。
其实直接用Windows API函数也可以很方便地实现事件(Event)、信号灯(semaphore)控制技术。尤其是C++Builder,在调用Windows API方面有着其它语言无可比拟的优势。所用的函数主要有:CreateSemaphore()、CreateEvent()、WaitForSingleObject()、ReleaseSemaphore()、SetEvent()等等,这里就不赘述了。
本文结合Inprise(Borland)公司开发的强大的RAD工具C++Builder的编程,对Windows下的多线程编程作了比较全面的介绍。其实多线程的实现并不神秘,看了本文,你也可以编出自己的多线程程序,真正体会多任务操作系统的威力。
附:本文是本人在使用C++Builder一年来的一些实践体会。在完成自己的项目的同时,发现对多线程的编程一般的书籍都介绍得比较少,而实际应用中,多线程编程又是如此的重要,因此,本文通过对多线程编程比较全面的介绍,愿能达到抛砖引玉之效。
最近在写一个程序用到了多线程,所以对CB下的多线程有一定的学习。
现在把自己的一些心得讲一下。水平有限,写的很粗略,请大家见谅。
CB相对于VC来说,在CB下写多线程程序是很简单的。不仅是VCL中有TThread这个类。封装了那些关于多线程的WINDOW API。我觉得更方便的是他提供了
直接访问主VCL线程中对象的能力。可以很容易的和主线程中的窗体,控件
打交道。和单线程的方式没有太多区别。只是在有多个线程都要访问主线程
中的对象(比如访问同一个窗体上的StringGrid).只要用Thread的Synchronize方法来调用那段访问主VCL线程的代码(具体请看帮助),我们就不用担心访问冲突的问题了。而且对于多线程的同步和互斥,CB也对WINDOW 编程中那些机制进行了封装。比如对临界区CriticalSection封装为TCriticalSection.事件Event封装为TEvent.这些类相当简单好用。
下面就是我觉得比较重要的几点,供大家参考.
1。TThread的WaitFor方法。是等待一个线程返回。其返回值在这个线程里可以任意设定。以便在该线程返回的时候让调用他的线程知道他的运行情况。
在TThread的 OnTerminate事件中做线程的清除工作。他不是线程运行的一部分。
而是主VCL线程的一部分。所以在其中不能访问Thread的局部变量(如 int __thread i)
你可以把清楚代码写在这里,不用管现在在EXCUTE()方法执行到了哪个地方。
这么看起来有点类似于C++里的 finally 块的作用。
2。TEvent很重要。实现线程的同步。WaitFor(int Timeout)功能类似于
WINDOW API WaitforSingleObject().返回值包括:
其中参数Timeout可以设为INFINITE表示永久等待,但这样,程序很容易死在这里。
wrSignaled 该事件发生(成功返回).
wrTimeout 等待超时.
wrAbandoned 在该事件的超时期限到达前,该事件对象已经被毁灭了。.
wrError 在等待过程中有异常产生,要知道具体产生的错误要查看 TEvent的LastError
属性。
3 TCriticalSection
这个相当于WIN32编程中的临界区。
在多线程编程中,多个线程需要访问同一个公用变量的时候。
来保证访问的正确性。对公用变量访问的代码写在Enter();和Leave()之间。
比如有个公用变量 Count;
以下代码 :
TCriticalSection * pSection=new TCriticalSection();
pSection-> Enter();
Count++;
pSection-> Leave();
delete p;
Enter()方法进入临界区,对其中的公用变量加锁。
Leave()方法离开临界区,对其中的公用变量解锁。
4.TMultiReadExclusiveWriteSynchronizer
用来处理类似于多个生产者和多个消费者的问题。这里的消费者是指
对公用变量进行读操作的线程。
生产者是对公用变量进行写操作的线程。
好文章
CB安装目录examplesapp hreads里面有例子
不知道这篇文章哪儿有下载。
可以发一份给我吗?
LfSoft@163.COM
谢谢大家
Leighf(好好生活,天天向上!) ,这篇文章你不会复制下去啊,嘎懒的
二楼,三楼的这篇文章找得好
xuexi
5 Apr
请问什么是多线程,如何创建多线程,还请高手给个这方面的例子.
在C++Builer中多线程的实现
还在Dos时代,人们就在寻求一种多任务的实现。于是出现了TSR类型的后台驻留程序,比较有代表性的有Side Kick、Vsafe等优秀的TSR程序,这类程序的出现和应用确实给用户使用计算机带来了极大的方便,比如Side Kick,我们编程可以在不用进编辑程序的状态下,一边编辑源程序,一边编译运行,非常方便。但是,Dos单任务操作系统的致命缺陷注定了在Dos下不可能开发出真正的多任务程序。进入Windows3.1时代,这种情况依然没有根本的改变,一次应用只能做一件事。比如数据库查询,除非应用编得很好,在查询期间整个系统将不响应用户的输入。
进入了Windows NT和Windows 9x时代,情况就有了彻底的改观,操作系统从真正意义上实现了多任务(严格地说,Win9x还算不上)。一个应用程序,在需要的时候可以有许多个执行线程,每个线程就是一个小的执行程序,操作系统自动使各个线程共享CPU资源,确保任一线程都不能使系统死锁。这样,在编程的时候,可以把费时间的任务移到后台,在前台用另一个线程接受用户的输入。对那些对实时性要求比较高的编程任务,如网络客户服务、串行通信等应用时,多线程的实现无疑大大地增强了程序的可用性和稳固性。
在Windows NT和Windows 9x中,多线程的编程实现需要调用一系列的API函数,如CreateThread、ResumeThread等,比较麻烦而且容易出错。我们使用Inprise公司的新一代RAD开发工具C++Builder,可以方便地实现多线程的编程。与老牌RAD工具Visual Basic和Delphi比,C++Builer不仅功能非常强大,而且它的编程语言是C++,对于系统开发语言是C的Windows系列操作系统,它具有其它编程语言无可比拟的优势。利用C++Builder提供的TThread对象,多线程的编程变得非常简便易用。那么,如何实现呢?且待我慢慢道来,让你体会一下多线程的强大功能。
1. 创建多线程程序:
首先,先介绍一下实现多线程的具体步骤。在C++Builder中虽然用Tthread对象说明了线程的概念,但是Tthread对象本身并不完整,需要在TThread下新建其子类,并重载Execute方法来使用线程对象。在C++Builder下可以很方便地实现这一点。
在C++Builder IDE环境下选择菜单File|New,在New栏中选中Thread Object,按OK,接下来弹出输入框,输入TThread对象子类的名字MyThread,这样C++Builder自动为你创建了一个名为TMyThread的TThread子类。同时编辑器中多了一个名为Unit2.cpp的单元,这就是我们创建的TMyThread子类的原码,如下:
#include
#pragma hdrstop
#include “Unit2.h”
#pragma package(smart_init)
//---------------------
// Important: Methods and properties of objects in VCL can only be
// used in a method called using Synchronize, for example:
//
// Synchronize(UpdateCaption);
//
// where UpdateCaption could look like:
//
// void __fastcall MyThread::UpdateCaption()
// {
// Form1-> Caption = “Updated in a thread”;
// }
//--------------------
__fastcall MyThread::MyThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//--------------------
void __fastcall MyThread::Execute()
{
//---- Place thread code here ----
}
//---------------------
其中的Execute()函数就是我们要在线程中实现的任务的代码所在处。在原代码中包含Unit2.cpp,这个由我们创建的TMyThread对象就可以使用了。使用时,动态创建一个TMyThread 对象,在构造函数中使用Resume()方法,那么程序中就增加了一个新的我们自己定义的线程TMyThread,具体执行的代码就是Execute()方法重载的代码。要加载更多的线程,没关系,只要继续创建需要数量的TMyThread 对象就成。
以上我们初步地实现了在程序中创建一个自定义的线程,并使程序实现了多线程应用。但是,多线程应用的实现,并不是一件简单的工作,还需要考虑很多使多个线程能在系统中共存、互不影响的因素。比如,程序中公共变量的访问、资源的分配,如果处理不当,不仅线程会死锁陷入混乱,甚至可能会造成系统崩溃。总的来讲,在多线程编程中要注意共享对象和数据的处理,不能忽视。因此,下面我们要讲的就是多线程中常见问题:
2. 多线程中VCL对象的使用
我们都知道,C++Builder编程是建立在VCL类库的基础上的。在程序中经常需要访问VCL对象的属性和方法。不幸的是,VCL类库并不保证其中对象的属性和方法是线程访问安全的(Thread_safe),访问VCL对象的属性或调用其方法可能会访问到不被别的线程所保护的内存区域而产生错误。因此,TThread对象提供了一个Synchronize方法,当需要在线程中访问VCL对象属性或调用方法时,通过Synchronize方法来访问属性或调用方法就能避免冲突,使各个线程之间协调而不会产生意外的错误。如下所示:
void __fastcall TMyThread::PushTheButton(void)
{
Button1-> Click();
}
void __fastcall TMyThread::Execute()
{
…
Synchronize((TThreadMethod)PushTheButton);
…
}
对Button1-〉Click()方法的调用就是通过Synchronize()方法来实现的,它可以自动避免发生多线程访问冲突。在C++Builder中,虽然有一些VCL对象也是线程访问安全的(如TFont、TPen、TBrush等),可以不用Sychronize()方法对它们的属性方法进行访问调用以提高程序性能,但是,对于更多的无法确定的VCL对象,还是强烈建议使用Synchronize()方法确保程序的可靠性。
3. 多线程中公共数据的使用
程序设计中难免要在多个线程中共享数据或者对象。为了避免在多线程中因为同时访问了公共数据块而造成灾难性的后果,我们需要对公共数据块进行保护,直到一个线程对它的访问结束为止。这可以通过临界区域(Critical Section)的使用来实现,所幸的是在C++Builder中,给我们提供了一个TCriticalSection对象来进行临界区域的划定。该对象有两个方法,Acquire()和Release()。它设定的临界区域可以保证一次只有一个线程对该区域进行访问。如下例所示:
class MyThread : public TThread
{
…
private:
TCriticalSection pLockX;
int x;
float y;
…
};
void __fastcall MyThread::Execute()
{
…
pLockX-> Acquire();//Here pLockX is a Global CriticalSection variable.
x++;
y=sin(x);
pLockX-> Release();
…
}
这样,对公共变量x,y的访问就通过全局TCriticalSection 对象保护起来,避免了多个线程同时访问的冲突。
4. 多线程间的同步
当程序中多个线程同时运行,难免要遇到使用同一系统资源,或者一个线程的运行要依赖另一个线程的完成等等,这样需要在线程间进行同步的问题。由于线程同时运行,无法从程序本身来决定运行的先后快慢,使得线程的同步看起来很难实现。所幸的是Windows系统是多任务操作系统,系统内核为我们提供了事件(Event)、Mutex、信号灯(semaphore)和计时器4种对象来控制线程间的同步。在C++Builder中,为我们提供了用于创建Event的TEvent 对象供我们使用。
当程序中一个线程的运行要等待一项特定的操作的完成而不是等待一个特定的线程完成时,我们就可以很方便地用TEvent对象来实现这个目标。首先创建一个全局的TEvent对象作为所有线程可监测的标志。当一个线程完成某项特定的操作时,调用TEvent对象的SetEvent()方法,这样将设置这个标志,其他的线程可以通过监测这个标志获知操作的完成。相反,要取消这个标志,可以调用ResetEvent()方法。在需要等待操作完成的线程中使用WaitFor()方法,将一直等待这个标志被设置为止。注意WaitFor()方法的参数是等待标志设置的时间,一般用INFINITE表示无限等待事件的发生,如果其它线程运行有误,很容易使这个线程死住(等待一个永不发生的事件)。
其实直接用Windows API函数也可以很方便地实现事件(Event)、信号灯(semaphore)控制技术。尤其是C++Builder,在调用Windows API方面有着其它语言无可比拟的优势。所用的函数主要有:CreateSemaphore()、CreateEvent()、WaitForSingleObject()、ReleaseSemaphore()、SetEvent()等等,这里就不赘述了。
本文结合Inprise(Borland)公司开发的强大的RAD工具C++Builder的编程,对Windows下的多线程编程作了比较全面的介绍。其实多线程的实现并不神秘,看了本文,你也可以编出自己的多线程程序,真正体会多任务操作系统的威力。
附:本文是本人在使用C++Builder一年来的一些实践体会。在完成自己的项目的同时,发现对多线程的编程一般的书籍都介绍得比较少,而实际应用中,多线程编程又是如此的重要,因此,本文通过对多线程编程比较全面的介绍,愿能达到抛砖引玉之效。
最近在写一个程序用到了多线程,所以对CB下的多线程有一定的学习。
现在把自己的一些心得讲一下。水平有限,写的很粗略,请大家见谅。
CB相对于VC来说,在CB下写多线程程序是很简单的。不仅是VCL中有TThread这个类。封装了那些关于多线程的WINDOW API。我觉得更方便的是他提供了
直接访问主VCL线程中对象的能力。可以很容易的和主线程中的窗体,控件
打交道。和单线程的方式没有太多区别。只是在有多个线程都要访问主线程
中的对象(比如访问同一个窗体上的StringGrid).只要用Thread的Synchronize方法来调用那段访问主VCL线程的代码(具体请看帮助),我们就不用担心访问冲突的问题了。而且对于多线程的同步和互斥,CB也对WINDOW 编程中那些机制进行了封装。比如对临界区CriticalSection封装为TCriticalSection.事件Event封装为TEvent.这些类相当简单好用。
下面就是我觉得比较重要的几点,供大家参考.
1。TThread的WaitFor方法。是等待一个线程返回。其返回值在这个线程里可以任意设定。以便在该线程返回的时候让调用他的线程知道他的运行情况。
在TThread的 OnTerminate事件中做线程的清除工作。他不是线程运行的一部分。
而是主VCL线程的一部分。所以在其中不能访问Thread的局部变量(如 int __thread i)
你可以把清楚代码写在这里,不用管现在在EXCUTE()方法执行到了哪个地方。
这么看起来有点类似于C++里的 finally 块的作用。
2。TEvent很重要。实现线程的同步。WaitFor(int Timeout)功能类似于
WINDOW API WaitforSingleObject().返回值包括:
其中参数Timeout可以设为INFINITE表示永久等待,但这样,程序很容易死在这里。
wrSignaled 该事件发生(成功返回).
wrTimeout 等待超时.
wrAbandoned 在该事件的超时期限到达前,该事件对象已经被毁灭了。.
wrError 在等待过程中有异常产生,要知道具体产生的错误要查看 TEvent的LastError
属性。
3 TCriticalSection
这个相当于WIN32编程中的临界区。
在多线程编程中,多个线程需要访问同一个公用变量的时候。
来保证访问的正确性。对公用变量访问的代码写在Enter();和Leave()之间。
比如有个公用变量 Count;
以下代码 :
TCriticalSection * pSection=new TCriticalSection();
pSection-> Enter();
Count++;
pSection-> Leave();
delete p;
Enter()方法进入临界区,对其中的公用变量加锁。
Leave()方法离开临界区,对其中的公用变量解锁。
4.TMultiReadExclusiveWriteSynchronizer
用来处理类似于多个生产者和多个消费者的问题。这里的消费者是指
对公用变量进行读操作的线程。
生产者是对公用变量进行写操作的线程。
好文章
CB安装目录examplesapp hreads里面有例子
不知道这篇文章哪儿有下载。
可以发一份给我吗?
LfSoft@163.COM
谢谢大家
Leighf(好好生活,天天向上!) ,这篇文章你不会复制下去啊,嘎懒的
二楼,三楼的这篇文章找得好
xuexi
22 Feb
.h文件
typedef struct
{
TGroupBox *gb;
TRichEdit *RichEdit1;
TImage *img;
}TInst;
.cpp文件
TForm1 *Form1;
TInst *inst=new TInst();
//—————————————————————————
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//—————————————————————————
void __fastcall TForm1::Button1Click(TObject *Sender)
{
inst->gb = new TGroupBox(this);
inst->gb->Caption = "Log";
inst->gb->Parent = Form1;
inst->gb->Top =Form1->Top+20;
inst->gb->Left =Form1->Left +20;
inst->gb->Width =800;
inst->gb->Height=800;
inst->RichEdit1= new TRichEdit(inst->gb);
inst->RichEdit1->Parent =inst->gb;
inst->RichEdit1->Top =inst->gb->Top+10;
inst->RichEdit1->Left =inst->gb->Left+10;
inst->RichEdit1->Width=600;
inst->RichEdit1->Height =650;
inst->img=new TImage(inst->RichEdit1 );
inst->img->Parent =inst->RichEdit1 ;
inst->img->Top =inst->RichEdit1->Top +10;
inst->img->Left =inst->RichEdit1->Left +10;
inst->img->Width =300;
inst->img->Height=300;
inst->img->Stretch =true;
inst->img->Transparent =true;
inst->img->Picture=this->Image1->Picture ;
我想让我创建的img 在RichEdit1,但现在img 的位置随RichEdit1输入而向下移动,且RichEdit1输入也有问题,请指教?
}
RichEdit1加载图片不是如此使用的.
严重支持 songhtao
3 Jan
在BCB2009中如何创建IntraWEB程序?
在以前的版本中,有创建IntraWEB程序的向导,为什么现在我在BCB2009中找不到了?但是,那些IW…组件还在。
我找了以前创建的一个IntraWEB小程序用BCB2009编译,执行,也能够通过。
但我要想创建新的IntraWEB工程的时候,确不知道在什么地创建了。
请知道的告诉一声。
我的BCB2009的BDS.EXE的版本为:12.0.3420.21218
补丁打到了UPDATE 4
New一个,好像在new/customize中可以定制的。
我看了N次了,在那儿真的没有
顺便提一下,在New->VCL For the WEB Application wizard C++Builder
这个貌似我想要的那个东西。但是,用它创建应用程序以后,IW…那些组件只有极少的几个显示了,其它的都不再显示。所以我估计这个东西也不是我想要的东东。
能不能详细说一下啊?
为什么选择New->VCL For the WEB Application wizard C++Builder
的时候IW…只剩下几个能显示,其它大部分都不显示了啊?
自己再顶一下吧
23 Dec
是这样的,比如我把form2设为available了,然后在form1上按button1的时候动态创建了form2了,再按一下又动态创建了form2,这个时候我把第一个动态创建的form2关掉了,但是不释放内存,我现在要再调用那个form应该怎么办啊,如果Form->show()的话出现的应该是第二个创建的form啊
你new出两个窗口出来,要保存它们的指针,通过指针就可以访问了。也是通过这个指针来释放。
如果你自己不保存动态创建对象的指针,我也想不出来有什么办法可以访问对象
用数组吧
form2设为available了,你把form2想象成一个资源,你new了一个form2出来,当你继续点击button时,先做个判断,如果form2已经new给一个指针了,就不要new新的了。