none
visual 2010 C++ form backgroundWorker 請教 RRS feed

  • 問題

  • 我的問題是,backgroundWorker跟UI互動的問題

    dowork小弟我是這樣寫,我是參考Bill Chung 的 所以應該沒有問題

    private: System::Void backgroundWorker1_DoWork(System::Object^  sender, System::ComponentModel::DoWorkEventArgs^  e)
    {
    //progressBar2->Value = 100;
    for (; ; )
                {
                    if (backgroundWorker1->CancellationPending == true)
                    {
                        e->Cancel = true;
                        break;
                    }
                    else
                    {
                        try
                        {
                            backgroundWorker1->ReportProgress(0);
                            System::Threading::Thread::Sleep(cycle);
                        }
                        catch (Exception^ )
                        {
                            e->Cancel = true;
                            break;
                        }
                    }
                }
    }

    (1)再來是ProgressChanged的部分,這是我原本的寫法,這樣寫可以正確抓資料,但UI會卡死

    private: System::Void backgroundWorker1_ProgressChanged(Object^  sender, System::ComponentModel::ProgressChangedEventArgs^  e)
    {
    unsigned char k[16];

    if(this->MyserialPort->IsOpen)
    {
    try

     //this->textBox4->Text = String::Empty;
    for(int j=0; j<16; j++)
    {
    k[j]=this->MyserialPort->ReadByte();
    }

    this->textBox4->Text = System::Convert::ToString(k[0],16);

    }
    catch(TimeoutException^)
    {
    this->textBox1->Text = String::Empty;
    this->textBox1->Text="Timeout Exception";
    }

      }

    }

    (2)因為之前就有問過類似的問題,我改成這樣,的確可以正確抓值
    然後不會把UI卡死

    但我覺得怪怪的,可以請各位大大幫小弟我看看
    哪裡需要改一下比較好

    private: System::Void backgroundWorker1_ProgressChanged(Object^  sender, System::ComponentModel::ProgressChangedEventArgs^  e)
    {
    unsigned char k[16];

    if(this->MyserialPort->IsOpen)
    {
    try

     //this->textBox4->Text = String::Empty;
    for(int j=0; j<16; j++)
    {
    UpdateResult(j);<----------------------------------------------------------我主要改這
    }

    this->textBox4->Text = System::Convert::ToString(k[0],16);
    //this->progressBar2->Value = 100;  

    }
    catch(TimeoutException^)
    {
    this->textBox1->Text = String::Empty;
    this->textBox1->Text="Timeout Exception";
    }
     
      }
    //backgroundWorker1->RunWorkerAsync();

    }

     private: void UpdateResult(int k)
        {
    unsigned char s[1];
            if (this->textBox4->InvokeRequired)
            {
    s[k]=this->MyserialPort->ReadByte();
    //this->textBox4->Text = System::Convert::ToString(s[k],16);
            }
            else
               ; 
        }
    2014年10月28日 下午 02:28

解答

所有回覆

  • 補充:抓進來值是有問題的..該如何改?
    2014年10月28日 下午 03:10
  • 把for迴圈移到UpdateResult()函式裡去做...
    2014年10月29日 上午 02:07
  • 您好,我想請教 private: void UpdateResult(int k) { unsigned char s[1]; if (this->textBox4->InvokeRequired) { s[k]=this->MyserialPort->ReadByte(); <-------------------這行讀取完 this->textBox4->Text = System::Convert::ToString(s[k],16); <---------------為什麼程式不會執行這一行? } else ; }
    2014年10月29日 上午 05:32
  • How to: Make Thread-Safe Calls to Windows Forms Controls

    http://msdn.microsoft.com/en-us/library/vstudio/ms171728(v=vs.100).aspx

    委派試試...

    2014年10月29日 上午 06:12
  • 這個部份我有看過了...

    因為我是初學者..我先用backgroundWorker吧,委派的方式更高竿且複雜

    我一時沒有辦法用董

    我目前這樣寫,抓進來的資料才是正常的

    但UI介面就是會卡住,我該如何修改

    這方面我找了很多資料,但都是以顯示文字在textbox上的範例..

    我有試過可以,但如果加入serial port的部分,我真不知道哪裡有問題..

    我需要修改什麼地方?懇求各位大師教導..

    private: System::Void backgroundWorker1_ProgressChanged(Object^  sender, System::ComponentModel::ProgressChangedEventArgs^  e)
    {
     
    unsigned char k[16]={0};

    if(this->MyserialPort->IsOpen)
    {
    try

    for(int j=0;j<16;j++)
    {
    k[j]=this->MyserialPort->ReadByte();
    }
    this->textBox4->Text = System::Convert::ToString(k[0],16);
    this->textBox5->Text = System::Convert::ToString(k[1],16);
    this->textBox6->Text = System::Convert::ToString(k[2],16);
    this->textBox7->Text = System::Convert::ToString(k[3],16);
    this->textBox8->Text = System::Convert::ToString(k[4],16);
    this->textBox9->Text = System::Convert::ToString(k[5],16);
    this->textBox10->Text = System::Convert::ToString(k[6],16);
    this->textBox11->Text = System::Convert::ToString(k[7],16);
    this->textBox12->Text = System::Convert::ToString(k[8],16);
    this->textBox13->Text = System::Convert::ToString(k[9],16);
    this->textBox14->Text = System::Convert::ToString(k[10],16);
    this->textBox15->Text = System::Convert::ToString(k[11],16);
    this->textBox16->Text = System::Convert::ToString(k[12],16);
    this->textBox17->Text = System::Convert::ToString(k[13],16);
    this->textBox18->Text = System::Convert::ToString(k[14],16);
    this->textBox19->Text = System::Convert::ToString(k[15],16);
    }
    catch(TimeoutException^)
    {
    this->textBox1->Text = String::Empty;
    this->textBox1->Text="Timeout Exception";
    }
     
      }
    //backgroundWorker1->RunWorkerAsync();

    }



    2014年10月29日 上午 06:52
  • BackgroundWorker Class

    http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx

    在DoWork事件裡讀SerialPort;在ProgressChanged和RunWorkerCompleted事件裡,單純顯示TextBox試試...

    2014年10月29日 上午 07:28
  • backgroundWorker 是 GUI Thread 有空才會跑。

    所以要確認你的東西在 Work thread 跑,GUI Thread 才會閒閒沒事幹可以跑。


    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    2014年10月29日 下午 12:39
  • (1)serialport 不算UI介面的物件嗎?

    (2)我想問如果我在dowork裡面抓資料,那我要如何把抓進來的資料丟到ProgressChanged,讓值顯示在textbox上?
    private: System::Void backgroundWorker1_DoWork(System::Object^  sender, System::ComponentModel::DoWorkEventArgs^  e)
    {

    unsigned char k[16];
    int j;
    for(;;)
    {
                    if (backgroundWorker1->CancellationPending == true)
                    {
                        e->Cancel = true;
                        break;
                    }
                    else
                    {
                        try
                        {
    for(j=0;j<16;j++)
    {
    k[j]=this->MyserialPort->ReadByte();
    }
                            backgroundWorker1->ReportProgress(0,k[j]);
                            System::Threading::Thread::Sleep(cycle);
                        }
                        catch (Exception^ )
                        {
                            e->Cancel = true;
                            break;
                        }
                    }
    }
    }

    2014年10月29日 下午 01:15
  • 恩恩..

    心冷大您第二句說的意思是GUI Thread 在一直不段執行的情況下

    我其他事情要放
    Work thread去另外做吧!

    但您第一句說
    backgroundWorker 是 GUI Thread 有空才會跑,我有點不太懂..


    2014年10月29日 下午 01:45
  • Joe Hung大 謝謝你提供這個訊息
    不過就是有點小小挫折..
    我想先測試可以先順利的1和2過去

    但ProgressChanged那結果在textbox顯示出來的是" System.Byte[] "的字
    不是應該要顯示我設定的1or2嗎?

    \\DoWork
    {
    array<unsigned char>^ k = gcnew array<unsigned char> {1,2};

    for(;;)
    {
    if (backgroundWorker1->CancellationPending == true)
    {
    e->Cancel = true;
    break;
    }
    else
    {
    try
    {
    backgroundWorker1->ReportProgress(0,k);
    System::Threading::Thread::Sleep(cycle);
    }
    catch (Exception^ )
    {
    e->Cancel = true;
    break;
    }
                    }
    }
    }




    \\ProgressChanged
    {
    //unsigned char k[16]={0};
    progressBar2->Value = 100;
    array<unsigned char>^ k = dynamic_cast<array<unsigned char>^>(e->UserState);
    if(this->MyserialPort->IsOpen)
    {
    try


    this->textBox4->Text = System::Convert::ToString(k);

    }
    catch(TimeoutException^)
    {
    this->textBox1->Text = String::Empty;
    this->textBox1->Text="Timeout Exception";
    }

      }
    }

                                                                                                                                                                                                                                                                                                                                                    
    2014年10月29日 下午 03:00
  • 我主要是在DOWORK裡,抓serial port的資料

    是不是在ProgressChanged只能顯示進度

    serial port抓到資料的值,是不是要丟到RunWorkerCompleted去?

    2014年10月29日 下午 04:13
  • 1. 通常主執行緒會是 GUI Thread ,也會是主畫面建立的執行緒

    2. 若你程式中沒有特別建立執行緒的程式碼,都是用主執行緒去跑

    3. SerialPort

    a. 程式碼直接 Sleep / 讀,用當前執行緒跑 (有建立就是 Work ,沒建立就是 GUI)

    b. 用接收事件跑,.Net 自動會建立一條 Work Thread 去跑,因此,此執行緒不能控制 GUI Thread 的介面物件,例如 TextBox ,必須透過委派或 backgroundWorker 去跑。

    i. 不論是委派或是 backgroundworker ,本身 work thread 不能有獨佔 CPU 情形。

    ii. 若是 a + 委派或是 backgroundWorker ,由於 a 本身占用 GUI Thread ,輪不到 backgroundWorker 來更新畫面。

    所以在多緒下,除了考量執行緒彼此競速外,資源存取也是一個問題。work thread 可以有很多條,gui thread 只有一條。

    舊版 .Net 內建的 Samples 101 裡面有 MultiThreading 範例 ,有示範三種進度列視窗可參考,其中最順的就是委派,不是 backgroundWorker ,backgroundWorker 是微軟給人方便用,不過我覺得反而會搞亂,不如直接用委派。


    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    2014年10月29日 下午 05:45
  • 謝謝心冷大詳細講解

    a. 程式碼直接 Sleep / 讀,用當前執行緒跑 (有建立就是 Work ,沒建立就是 GUI)

    b. 用接收事件跑,.Net 自動會建立一條 Work Thread 去跑,因此,此執行緒不能控制 GUI Thread 的介面物件,例如 TextBox ,必須透過委派或 backgroundWorker 去跑。

    這段我能理解的是,您說當serial port再開始接收時,如果要更新GUI上的物件,就是一定要用另外一個執行緒去執行吧,是嗎?我能理解的大概就是這樣

    就像心冷大說的,的確我覺得backgroundworker如果要達到我需要的功能,真的不太容易
    當然也是我個人經驗不足

    .Net 內建的 Samples 用委派的方式,我明天馬上寫,有問題就發上來
    b62856285@gmail.com 我方便留您聯絡的mail嗎 ?

    2014年10月29日 下午 06:43
  • for(int i = 0; i < 2; ++i)		
    {	
            this->textBox1->Text += System::Convert::ToString(k[i]);	
    }

    2014年10月30日 上午 03:00
  • 還是不行..
    textox4顯示Cancelled,這是什麼意思?


    \\DoWork
    {

    array<unsigned char>^ k = gcnew array<unsigned char> {0};
    int j;
    for(;;)

    {
                    if (backgroundWorker1->CancellationPending == true)
                    {
                        e->Cancel = true;
                        break;
                    }
                    else
                    {
                        try
                        {
    for(int j=0; j<16; j++)
     {
      k[j] =this->MyserialPort->ReadByte();
     }

                            backgroundWorker1->ReportProgress(10,k[0]);
                            System::Threading::Thread::Sleep(cycle);
                        }
                        catch (Exception^ )
                        {
                            e->Cancel = true;
                            break;
                        }
                    }
    }
    }



    \\ProgressChanged
    {
    this->progressBar2->Value = e->ProgressPercentage;

    array<unsigned char>^ k = safe_cast<array<unsigned char>^>(e->UserState);

    if(this->MyserialPort->IsOpen)
    {
    try

    for(int i = 0; i < 2; ++i)
    {
    this->textBox4->Text += System::Convert::ToString(k[i]);
    }

    }
    catch(TimeoutException^)
    {
    this->textBox1->Text = String::Empty;
    this->textBox1->Text="Timeout Exception";
    }

      }
    }

     
    2014年10月30日 上午 03:16
  • backgroundWorker1->ReportProgress(10,k);


    2014年10月30日 上午 06:14
  • 結果還是一樣,textox4顯示Cancelled。
    2014年10月30日 下午 12:34
  • 我再次看了一次MSDN上,講解多執行緒委派方式的範例

    我把以前想問的問題問一問好了

    下面這段
    private:
            void SetText(String^ text)
            {
                // InvokeRequired required compares the thread ID of the
                // calling thread to the thread ID of the creating thread.
                // If these threads are different, it returns true.
                if (this->textBox20->InvokeRequired)
                {
                    SetTextDelegate^ d = gcnew SetTextDelegate(this, &Form1::SetText);
    //delegate關鍵字建立SetTextDelegate委派後,在使用SetTextDelegate建立物件d
    //而這個d是指向&Form1::SetText這個方法位址的物件



                    this->Invoke(d, gcnew array<Object^> { text });

     this->Invoke(d, gcnew array<Object^> { text }); 這行裡的gcnew array<Object^> { text }是什麼意思?要如何使用?



                }
                else
                {
                    this->textBox20->Text = text;
                }
            }
    2014年10月30日 下午 12:42
  • Control::Invoke 方法 (Delegate, array<Object>)

    http://msdn.microsoft.com/zh-tw/library/a1hetckb(v=vs.110).aspx

    做為引數傳遞至指定方法的物件陣列。text宣告為String,所以當接收Byte陣列時,需轉換為String,再傳到SetText,TextBox就會顯示字串了...

    2014年10月30日 下午 11:30
  • Joe Hung 大您的意思是說

    this->SetText("This text was set safely.");//這段在呼叫SetText時

     

    private:void SetText(String^ text)

    {

    if (this->textBox1->InvokeRequired)<----------這行會先判斷 { SetTextDelegate^ d = gcnew SetTextDelegate(this, &Form1::SetText);

    this->Invoke(d, gcnewarray<Object^> { text });

    所以這一段是在把原本丟進來的資料轉換成String後,在重新Invoke回SetText去

    就會符合else裡的條件,顯示在textbox上,是嗎?

    textBox1->InvokeRequired的InvokeRequired的意思是?我看MSDN的說明,半懂..

    } else { this->textBox1->Text = text; } }

    
    

    2014年10月31日 上午 09:26
  • Control::InvokeRequired 屬性

    http://msdn.microsoft.com/zh-tw/library/system.windows.forms.control.invokerequired(v=vs.110).aspx

    不是,SetText函式宣告傳入為String,呼叫時就必須是String,不然編譯就會錯誤,InvokeRequired會指示是否由於呼叫端是在建立控制項之執行緒以外的執行緒,

    如不同執行緒,就會執行if內的指令,else是相同執行緒才會執行...

    • 已標示為解答 Ervin6285 2014年11月4日 上午 02:59
    2014年10月31日 上午 11:09
  • Joe Hung大
    我最近在看這篇的範例
    http://www.dotblogs.com.tw/billchung/archive/2012/01/20/66860.aspx

    我要轉成C++

    private: System::Void button7_Click(System::Object^  sender, System::EventArgs^  e) 
    {
                comport->DataReceived +=gcnew SerialDataReceivedEventHandler(&serialportstest::Form1::comport_DataReceived);
                this->comport->Open();
    }
     
    private: void comport_DataReceived(Object^ sender, SerialDataReceivedEventArgs^ e)
            {

            }

    我轉到這,編譯一直會跑出
     error C3352: 'void serialportstest::Form1::comport_DataReceived(System::Object ^,System::IO::Ports::SerialDataReceivedEventArgs ^)' : 指定的函式不符合委派型別 'void (System::Object ^,System::IO::Ports::SerialDataReceivedEventArgs ^)'

    這是什麼問題?我搞不太懂..

    2014年11月3日 上午 05:15
  • comport->DataReceived += gcnew SerialDataReceivedEventHandler(this, &Form1::comport_DataReceived);


    • 已標示為解答 Ervin6285 2014年11月4日 上午 02:59
    2014年11月3日 上午 05:42
  • Joe Hung大,謝謝你,我已經轉完了

    資料是一次會有16筆資料進來,
    textBox20也顯示了16比資料,因為架構的部分我還不是很了解
    所以在textBox20上顯示的資料是一次顯示16筆沒錯

    但我希望能夠把這16筆分開來各別顯示在16個textBox上

    請問我要怎麼修改?


    private: void SerialPort_DataReceived(Object^ sender,SerialDataReceivedEventArgs^ e)
            {
                buffer = gcnew array<Byte>(1024);
                int length = SerialPort->Read(buffer, 0, buffer->Length);
                Array::Resize(buffer, length);//將一維陣列中的元素數目變更為指定的新大小。
                Display^ d = gcnew Display(this,&Form1::DisplayText);
                this->Invoke(d,gcnew Object(){buffer});
            }
     
    private: void DisplayText(array<Byte>^ buffer)
            {
        totalLength = 0;
                textBox20->Text += String::Format("{0}{1}", BitConverter::ToString(buffer), Environment::NewLine);
                totalLength = totalLength + buffer->Length;
                textBox21->Text = totalLength.ToString();
            }
    2014年11月3日 上午 08:01
  • for(int i = 0; i < buffer->Length; ++i)				 
    {				
            TextBox^ tx =safe_cast<TextBox^>(this->Controls["textBox" +Convert::ToString( i+1)]);				
            tx->Text =Convert::ToString( buffer[i]);				 
    }

    C++/CLI - Get current control by name

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/e49974c4-d630-4b7e-b7b6-105c91b132bf/ccli-get-current-control-by-name?forum=vclanguage

    • 已標示為解答 Ervin6285 2014年11月4日 上午 02:59
    2014年11月3日 上午 08:39
  • 謝謝Joe Hung大 

    for(int i = 0; i < buffer->Length; i++)
    {
    TextBox^ tx =safe_cast<TextBox^>(this->Controls["textBox" +Convert::ToString(i+5)]);
    tx->Text =Convert::ToString(buffer[i],16);
    }

      因為我是從textbox5開始,所以我改成上面這樣

    但因為我只看懂"textBox" +Convert::ToString(i+5)這段是在選擇哪一個textbox

    前面TextBox^ tx =safe_cast<TextBox^>(this->Controls 我看不太懂? 可以麻煩Joe Hung大 講解一下嗎..哈哈

    2014年11月3日 上午 09:10
    • 已標示為解答 Ervin6285 2014年11月4日 上午 02:59
    2014年11月3日 上午 09:29
  • 謝謝Joe Hung 分享

    這邊我看懂safe_cast<TextBox^>(this->Controls["textBox" +Convert::ToString(i+5)]);

    是把
    (this->Controls["textBox" +Convert::ToString(i+5)])這誇號裡頭的資料轉成TextBox^物件

    但我看了範例,就有點不懂
    Controls裡的["textBox" +Convert::ToString(i+5)]);為什麼是這樣表示 = =...

    "textBox"+Convert::ToString(i+5)] ?? textBox不是UI上一個物件嗎?然後加上Convert::ToString(i+5)??

    初學者腦袋不懂專家的世界..懇求解釋..還是說這就只是個小技巧.. -..-



    • 已編輯 Ervin6285 2014年11月3日 下午 06:22
    2014年11月3日 下午 05:45
  • 很簡單,一點都不會專業,i是整數變數,要轉成字串,合成"textBox5"字串去找,您的基礎沒打好,一些基本的變數型態概念都沒有,有買書來看嗎...
    • 已標示為解答 Ervin6285 2014年11月4日 上午 02:58
    • 已取消標示為解答 Ervin6285 2014年11月4日 上午 02:58
    2014年11月4日 上午 01:14
  • 哈哈 Joe Hung大 我承認,以前太混..
    也只是寫寫DEV C交交作業,沒有特別去研究
    直接就碰visual C++ form 了 
    不過我找的書,都只是簡單的範例,
    並沒有這樣的寫法,這邊我要先結案
    有另外問題要另外發問了 謝謝你~ :)


    • 已編輯 Ervin6285 2014年11月4日 上午 03:01
    2014年11月4日 上午 02:58