65.9K
CodeProject 正在变化。 阅读更多。
Home

托管 C++ 中的带 Windows Forms 控件的线程

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.33/5 (12投票s)

2006年1月17日

CPOL

4分钟阅读

viewsIcon

99904

downloadIcon

1930

本文介绍了带 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日:首次发布
© . All rights reserved.