0

请看下面的代码

#pragma once


    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Threading;

    /// <summary>
    /// Summary for NotifyAlarm
    /// </summary>
    public ref class NotifyAlarm : public System::Windows::Forms::Form
    {
        int count;


    public:
        NotifyAlarm(void)
        {
            InitializeComponent();
            //
            //TODO: Add the constructor code here
            //
            count = 10;
        }

    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~NotifyAlarm()
        {
            if (components)
            {
                delete components;
            }
        }
    private: System::Windows::Forms::Label^  label1;
    protected: 
    private: System::Windows::Forms::Label^  secondsLabel;
    private: System::Windows::Forms::Label^  label2;
    private: System::Windows::Forms::Button^  sendNowBtn;
    private: System::Windows::Forms::Button^  cancelBtn;
    private: System::Windows::Forms::Timer^  timer1;
    private: System::ComponentModel::IContainer^  components;


    private:
        /// <summary>
        /// Required designer variable.
        /// </summary>


#pragma region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        void InitializeComponent(void)
        {
            this->components = (gcnew System::ComponentModel::Container());
            this->label1 = (gcnew System::Windows::Forms::Label());
            this->secondsLabel = (gcnew System::Windows::Forms::Label());
            this->label2 = (gcnew System::Windows::Forms::Label());
            this->sendNowBtn = (gcnew System::Windows::Forms::Button());
            this->cancelBtn = (gcnew System::Windows::Forms::Button());
            this->timer1 = (gcnew System::Windows::Forms::Timer(this->components));
            this->SuspendLayout();
            // 
            // label1
            // 
            this->label1->AutoSize = true;
            this->label1->Font = (gcnew System::Drawing::Font(L"Microsoft Sans Serif", 12, System::Drawing::FontStyle::Regular, System::Drawing::GraphicsUnit::Point, 
                static_cast<System::Byte>(0)));
            this->label1->Location = System::Drawing::Point(13, 27);
            this->label1->Name = L"label1";
            this->label1->Size = System::Drawing::Size(405, 20);
            this->label1->TabIndex = 0;
            this->label1->Text = L"Intruder Detected. An Email and SMS will be sent within ";
            // 
            // secondsLabel
            // 
            this->secondsLabel->AutoSize = true;
            this->secondsLabel->Font = (gcnew System::Drawing::Font(L"Microsoft Sans Serif", 12, System::Drawing::FontStyle::Regular, System::Drawing::GraphicsUnit::Point, 
                static_cast<System::Byte>(0)));
            this->secondsLabel->ForeColor = System::Drawing::Color::Red;
            this->secondsLabel->Location = System::Drawing::Point(408, 27);
            this->secondsLabel->Name = L"secondsLabel";
            this->secondsLabel->Size = System::Drawing::Size(51, 20);
            this->secondsLabel->TabIndex = 1;
            this->secondsLabel->Text = L"label2";
            // 
            // label2
            // 
            this->label2->AutoSize = true;
            this->label2->Font = (gcnew System::Drawing::Font(L"Microsoft Sans Serif", 12, System::Drawing::FontStyle::Regular, System::Drawing::GraphicsUnit::Point, 
                static_cast<System::Byte>(0)));
            this->label2->Location = System::Drawing::Point(465, 27);
            this->label2->Name = L"label2";
            this->label2->Size = System::Drawing::Size(69, 20);
            this->label2->TabIndex = 2;
            this->label2->Text = L"seconds";
            // 
            // sendNowBtn
            // 
            this->sendNowBtn->Location = System::Drawing::Point(370, 70);
            this->sendNowBtn->Name = L"sendNowBtn";
            this->sendNowBtn->Size = System::Drawing::Size(75, 23);
            this->sendNowBtn->TabIndex = 3;
            this->sendNowBtn->Text = L"Send Now";
            this->sendNowBtn->UseVisualStyleBackColor = true;
            this->sendNowBtn->Click += gcnew System::EventHandler(this, &NotifyAlarm::sendNowBtn_Click);
            // 
            // cancelBtn
            // 
            this->cancelBtn->Location = System::Drawing::Point(469, 70);
            this->cancelBtn->Name = L"cancelBtn";
            this->cancelBtn->Size = System::Drawing::Size(75, 23);
            this->cancelBtn->TabIndex = 4;
            this->cancelBtn->Text = L"Cancel";
            this->cancelBtn->UseVisualStyleBackColor = true;
            this->cancelBtn->Click += gcnew System::EventHandler(this, &NotifyAlarm::cancelBtn_Click);
            // 
            // timer1
            // 
            this->timer1->Enabled = true;
            this->timer1->Interval = 1000;
            this->timer1->Tick += gcnew System::EventHandler(this, &NotifyAlarm::timer1_Tick);
            // 
            // NotifyAlarm
            // 
            this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
            this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            this->ClientSize = System::Drawing::Size(566, 105);
            this->Controls->Add(this->cancelBtn);
            this->Controls->Add(this->sendNowBtn);
            this->Controls->Add(this->label2);
            this->Controls->Add(this->secondsLabel);
            this->Controls->Add(this->label1);
            this->Name = L"NotifyAlarm";
            this->Text = L"NotifyAlarm";
            this->ResumeLayout(false);
            this->PerformLayout();

        }
#pragma endregion
    private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) 
             {
                 count--;

                 if(count>0 || count==0)
                 {
                     secondsLabel->Text = ""+count;
                 } 
                 else
                 {
                     //code removed
                 }
             }
private: System::Void sendNowBtn_Click(System::Object^  sender, System::EventArgs^  e) 
         {
             timer1->Stop();

            //code removed
         }

public: System::Void showGUI() 
                  {
                      this->Show();
                  }



};

这是一种通知框,我试图在新的线程中打开它。原因是,默认线程已经超载。这就是我从另一个线程调用它的方式

na = gcnew NotifyAlarm();
        Thread ^alertThread = gcnew Thread(gcnew System::Threading::ThreadStart(na,&NotifyAlarm::showGUI));
        alertThread->Start();

不幸的是,当我运行此线程时,出现以下错误

在此处输入图像描述

当代码到达这里时发生这种情况

 if(count>0 || count==0)
  {
        secondsLabel->Text = ""+count;
  } 

如您所见,我正在尝试更新其中的标签。

那么,我怎样才能让这个 GUI 表单在另一个线程中运行而不会出现这些错误呢?

PS:我不是来自 .NET 文化,而是来自 Java 和 C++。

4

2 回答 2

4

您应该使用BeginInvoke同步(调度)对 UI 线程的调用:

delegate void UpdateTextDelegate(int count);

private: void DoUpdateText(int count)
{
    ISynchronizeInvoke^ i = this;

    if (i->InvokeRequired)
    {
      UpdateTextDelegate^ tempDelegate =
        gcnew UpdateTextDelegate(this, &Form1::DoUpdateText);
      cli::array<System::Object^>^ args = gcnew cli::array<System::Object^>(1);
      args[0] = count;
      i->BeginInvoke(tempDelegate, args);
      return;
    }

    secondsLabel->Text = count.ToString();
}

DoUpdateText然后您可以从另一个线程中调用该方法。

于 2013-08-19T09:52:05.097 回答
3

目前尚不清楚 Text 属性分配发生在哪个线程上。但显然它在错误的线程上,您需要使用表单或标签的 BeginInvoke() 方法来编组调用。

请注意,在启动线程之前创建表单对象实例是有风险的。规则是实际创建窗口句柄(CreateHandle() 调用)的任何线程都是窗口的所有者。如果表单的构造函数意外创建了句柄,那可能是错误的线程。只需在线程方法上创建表单对象即可避免麻烦。

值得注意的是,这是多么危险。一个重要的麻烦制造者是 SystemEvents 类,许多控件订阅了 UserPreferenceChanged 事件。当用户更改主题设置时,他们这样做是为了重新绘制自己。在其他情况下也会触发此事件,锁定工作站是一个臭名昭著的麻烦来源。桌面开关可以触发事件。

SystemEvents 具有在正确线程上触发此事件的令人羡慕的任务。它不能,你给它两个线程可供选择。您的主 UI 线程和这个新的“gui 线程”。其中之一将在错误的线程上触发事件。结果是死锁。它可能在您的表单关闭并且线程不再存在之后很长时间发生。

这很难处理。还有更多的问题,窗口有自己的生命,并且与您应用程序中的其余窗口没有 Z 顺序关系。一个重要的问题是它习惯于在主线程拥有的窗口下显示自己。用户看不到。这种问题需要通过使其成为一个拥有的窗口来解决,这样才能保证它是顶部的。这也不起作用,当您调用 Show(owner) 重载时,您将再次获得 InvalidOperationException。

非常令人讨厌的问题,信息应该很清楚:不要这样做。永远不需要,程序的主线程可以处理任意数量的窗口。典型的错误是使用这样的通知窗口来隐藏在主线程上运行的代码中的缺陷并且花费太多时间,从而使 GUI 无响应。真正的解决方法是在工作线程上运行该代码。

于 2013-08-19T10:56:27.587 回答