托管 C++ 中的带 Windows Forms 控件的线程
本文介绍了带 Windows Forms 控件的托管线程。
引言
Threads 类用于创建和操作托管线程。在 .NET 环境中,它与所有 .NET 启用语言使用的类相同。 .NET 线程捕获所有在托管线程中执行的线程。我们无法从托管线程访问非托管线程。Thread.GetHashCode
用于查找托管线程。托管线程在 .NET 公共语言运行时环境中执行。Microsoft 不建议混合使用托管线程和非托管线程。
托管线程
当调用 System.Threading.Thread.Start
方法时,托管线程开始执行。此方法用于启动托管线程。当用户创建线程时,我们可以使用 ThreadStart
委托或 ParameterizedThreadStart
委托来创建线程。ThreadStart
委托用于在不带任何参数的情况下启动线程。我们可以使用 Start
方法传递参数。
ParameterizedThreadStart
委托用于创建线程,以将 Object
作为参数传递。如果我们多次调用 Start
方法,它将引发 ThreadStateException
异常。以下示例显示了如何创建托管线程
ref class ThreadClass
{
public:
static void ThraedClassMethod()
{
/// Do work
}
};
ThreadClass ^ objclass = gcnew ThreadClass;
ThreadStart^ mThread = gcnew ThreadStart(objclass,&objclass::ThreadClasMethod);
Thread^ newThread = gcnew Thread(mThread);
newThread->Start();
ThreadState
和 IsAlive
属性用于获取线程状态。但 Microsoft 不建议将这些方法用作线程同步。System.Threading.Thread.Sleep
方法用于使当前执行的线程休眠。我们无法从线程函数休眠其他线程。System.Threading.Timeout.Infinite
等待无限级别或在调用其他线程返回时。如果调用 System.Threading.Thread.Abort
,它将终止。
在托管线程中,我们将设置线程优先级,这与非托管线程非常相似。CLR 为默认级别分配 ThreadPriority.Normal
。我们可以使用 Thread.Priority
属性获取或设置任何线程的优先级。Abort
方法用于永久销毁托管线程。当从主方法调用 Abort
方法时,将引发 ThreadAbortException
。
带控件的 Windows Forms
Microsoft 在 .NET Framework 中引入了一套新的库,采用面向对象技术来创建 Windows 上的智能客户端应用程序。Windows Forms 与用于创建 Windows 的现有技术(如 MFC、ATL 或 WTL)无关。Windows Forms 支持整个 .NET Framework,并且多种语言共享相同的类库集以及 .NET 功能。例如,MFC 开发人员可以获得 .NET 的功能。Microsoft 允许开发人员将 MFC 和 Windows Forms 混合到单个项目中。新的 MFC 类用于访问 .NET Windows Forms 控件。
在我们的示例中,Windows Forms 在多个线程之间进行访问。进度条显示不同线程的进度。每个进度条都有一个名为 Thread
的独立按钮。如果我们单击各个按钮,线程将启动并单独运行。Thread
在单击事件中启动。
Thread^ newThread = gcnew Thread(gcnew ParameterizedThreadStart(&ThreadProc3 ));
newThread->Start(this);
parameteterizedThreadStart
用于将 Windows Forms 对象传递给线程函数。static
方法调用安全的线程函数。如果开发人员想更改由线程获取的 Windows Forms 控件的状态,我们应该声明一个委托。
delegate void ProgressBarCallback(System::Object ^obj);
此委托传递 Windows Forms 对象。我们更改每个进度条的状态。如果您想同步调用方法,可以调用以下代码
static void SafeThread4(System::Object ^obj)
{
Form1 ^ob = (Form1^) obj;
if(ob->progressBar4->InvokeRequired)
{
ProgressBarCallback ^d = gcnew ProgressBarCallback(SafeThread4);
ob->Invoke(d,gcnew array<System::Object^>{ob});
}
else
{
for ( int i = 1; i <= 10; i++ )
{
ob->progressBar4->PerformStep();
Thread::Sleep( 80 );
}
}
}
托管线程和异常
在 .NET Framework 2.0 中,CLR 会处理托管应用程序中的未处理异常。
托管线程在以下情况下会引发异常:
- 当用户调用 Abort 方法时,会引发
ThreadAbortException
。 - 当执行线程的应用程序域正在卸载时,会引发
AppDomainUnloadedException
。 - 当宿主进程通过引发内部异常终止线程时,CLR 会引发异常。
当我创建示例应用程序时,我尝试从线程函数访问控件类。但是,我遇到了以下异常
static void ThreadProc1(System::Object ^obj)
{
Form1 ^ob = (Form1^) obj;
for ( int i = 0; i < 10; i++ )
{
ob->progressBar1->PerformStep();
Thread::Sleep( 0 );
}
}
当我执行上述代码时,我得到以下异常
System.InvalidOperationException was unhandled
Message="Cross-thread operation not valid:
Control 'progressBar1' accessed from a thread other than
the thread it was created on."
Source="System.Windows.Forms"
它还会提供堆栈跟踪。
结论
每个应用程序域至少以一个线程开始。System.Threading.Thread
类用于在托管环境中创建一或多个线程。对于多线程应用程序,CPU 在单个进程中的线程之间切换。如果系统在多处理器环境中使用多个线程,线程切换会在多个处理器之间进行。Microsoft 不建议混合使用托管和非托管线程。托管线程会被垃圾回收。因此,开发人员无需关心清理资源。所以,我们在托管线程中不会遇到内存泄漏问题。要使用最佳实践创建托管线程,请阅读此文。
历史
- 2006年1月17日:首次发布