none
程式問題請教PART3 RRS feed

  • 問題

  • 在前面的文章裡,有向各位前輩請教了多執行緒的問題
    現在遇到了一點小狀況,小弟我在下方描述一下
    因為情況真的有點怪,小弟我只能描述的詳細一點,所以有點長,先謝謝各位前輩花時間閱讀

    因為之前每一個功能,我都是分開專案製作,先確定功能各別可以正常執行後
    現在要把所有功能整合在同一個專案裡。

    ※雖然在同一個專案裡,但我三個功能還是分三個button去執行
    ※我總共用了3個pictureBox


    影像:pictureBox1
    接收資料:pictureBox2
    雷射掃描器:pictureBox3

    第一步:主執行緒的部分是用openCV,讀取影像,顯示在pictureBox上

    第二步:SerialPort_DataReceived 接收資料,另外再用委派的方式更新了textBox上的物件

    初步結合了,第一步&第二步是成功的,執行的很正常
    ---------------------------------------------------------------------------------------------------

    接著我要結合前面有提到的"雷射掃描器"

    我一按下"雷射掃描器"的button,顯示影像pictureBox1的部分就停了下來,不再執行
    換成pictureBox3顯示雷射掃描器的data

    我再按一下"影像"的button,換成顯示雷射掃描器的pictureBox3停了下來

    於是...
    我寫了另外的一個執行緒去執行

    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) 

    {
     if (button1->Text == "Connect")
                {
                    if (comboBox1->SelectedIndex < 0)
                    {
                        MessageBox::Show("Please select a COM port.");
                        return;
                    }

                    if (urgctrl->Connect(Int32::Parse((comboBox1->Text)->Substring(3, 1)), 115200) == false)
                    {
                        MessageBox::Show("Connection fail.");
                        return;
                    }

                    button1->Text = "Disconnect";

                }
                else
                {
                    urgctrl->Disconnect();

                    button1->Text = "Connect";
                }
    }


    //enable checkbox checked
    private: System::Void checkBox1_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
    {
    if (urgctrl->IsConnected() == false)
    {
    MessageBox::Show("Please connect to a COM port.");
    checkBox1->Checked = false;
    return;
    }

    while (checkBox1->Checked == true)
    {
    Application::DoEvents();

    if (urgctrl->IsConnected() == false)
    {
    MessageBox::Show("Please connect to a COM port.");
    checkBox1->Checked = false;
    return;
    }


    Thread^ thr1 = gcnew Thread(gcnew ThreadStart(this, &URG1::Form1::DoReceive));
    thr1->IsBackground = true;
    thr1->Start();

    }

    picbox->Clear(Color::White);
    }

    private: void DoReceive()
            {
                array<int>^ data = gcnew array<int>(urgctrl->MaxBufferSize);
                Display^ d = gcnew Display(this,&Form1::DisplayText);
                this->Invoke(d,gcnew Object(){data});
            }
    private: void DisplayText(array<int>^ data)
            {
    int i;
    array<Point>^ output_point = gcnew array<Point>(682);
    urgctrl->Capture(data);
    for (i = 0; i <= 681; i += 1)
                {
                    output_point[i] = calculate_point((data[681-i] / 10), (urgctrl->Index2Radian(i + 44) * 180.0 / Math::PI));
                }
    //calculate_point((data[i] / 10)=把MM變CM 距離縮短,解析度放小
                //(urgctrl.Index2Radian(i + 44)//轉角度

    //clear picbox
                picbox->Clear(Color::White);
                picbox->DrawLines(Pens::Blue, output_point);//畫藍色的線

    }

    結果,狀況還是一樣...不能兩邊都一起執行,一定有一邊會停下來
    ---------------------------------------------------------------------------------------------------
    那好吧,我就想試試看...我就把,下面這三行,放進抓取影像的while(1)迴圈裡

    Thread^ thr1 = gcnew Thread(gcnew ThreadStart(this, &URG1::Form1::DoReceive));
    thr1->IsBackground = true;
    thr1->Start();

    的確,就解決上面我提到的問題,但是
    影像pictureBox1的部分&雷射掃描器:pictureBox3 就變得很頓
    感覺好像在同一個執行緒執行一樣...

    關於這一個問題,不知道要如何解決呢?

    2014年11月19日 上午 09:08

解答

  • 一、更新幾次的用意,是您對慢的底限可以接受到什麼程度?

    二、serialPort是用事件觸發,只接收幾個字元,顯示在文字盒,處理速度很快。

    三、請不要把平板Atom和桌上型多核心處理器效能相提並論...

    • 已標示為解答 Ervin6285 2014年11月22日 上午 03:41
    2014年11月22日 上午 02:43

所有回覆

  • 您可以在DoReceive函式中加人:

    Thread::Sleep(2000);

    再試看看

    2014年11月19日 上午 11:57
  • tihs 大

    我加了,沒有辦法,還是一樣。

    我覺得好像不是那個原因@@ 


    • 已編輯 Ervin6285 2014年11月19日 下午 12:52
    2014年11月19日 下午 12:52
  • 雷射掃描的部分
    我發現,他一定要加Application::DoEvents(); 這一行才能執行
    2014年11月19日 下午 01:09
  • 我附上雷射掃描.dll動態資料庫的方法
    public int MaxBufferSize { get; }
    public int MaxDistance { get; }
    public int MinDistance { get; }
    public int ScanMsec { get; }
    public int Capture(int[] data);
    public bool Connect(int ComPort, int baudrate);
    public void Disconnect();
    public int GetTimestamp();
    public string[] GetVersionInformation();
    public double Index2Radian(int index);
    public bool IsConnected();
    public int Radian2Index(double radian);
    public string what();
    2014年11月19日 下午 01:53
  • Thread 類別

    http://msdn.microsoft.com/zh-tw/library/system.threading.thread(v=vs.110).aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-2

    在控制項的事件裡的while迴圈,做成另一個執行緒,不然可能無法執行到DoReceive執行緒,每個執行緒迴圈裡,都要Sleep,別的執行緒才能運作...

    2014年11月20日 上午 02:23
  • 不是要混在一起,有幾個週邊設備,都可以各自獨立跑執行緒...
    2014年11月20日 上午 03:11
  • 	while (checkBox1->Checked == true)
    	{
    		Thread::Sleep (10);
    		Application::DoEvents();

    C# SerialPort.Read 會使CPU飆高問題

    https://social.msdn.microsoft.com/Forums/zh-TW/32816186-636a-43c0-abb9-395b876a0ac8/c-serialportread-cpu?forum=233

    貼程式請善用排版功能,請參考此篇討論;DisplayText1的for迴圈也加入Sleep;如果Sleep無改善,把while迴圈這段做成另一個執行緒...

    2014年11月20日 上午 04:22
  • while (checkBox1->Checked == true) {

    Thread::Sleep (500);

    Display2^ d = gcnew Display2(this,&Form1::DisplayText1); this->Invoke(d);

    }

    2014年11月20日 上午 05:21
  • 獨立做個執行緒;在每個for迴圈裡,Sleep(0)或1試試...
    2014年11月20日 上午 06:13
  • 我改成這樣,分成兩個執行緒
    private: System::Void button3_Click(System::Object^  sender, System::EventArgs^  e) 
    		 {
    		    if (button3->Text == "Connect")
                {
                  
    				urgctrl->Connect(Int32::Parse((comboBox3->Text)->Substring(3, 1)), 115200);
    				Thread^ thr1 = gcnew Thread(gcnew ThreadStart(this, &IntegrationSystem::Form1::DoReceive));
    				thr1->Start();
    				Thread^ thr2 = gcnew Thread(gcnew ThreadStart(this, &IntegrationSystem::Form1::DoReceive2));
    				thr2->Start();
                    button3->Text = "Disconnect";
    
                }
                else
                {
                    urgctrl->Disconnect();//結束連線
    
                    button3->Text = "Connect";
                }
    		 }

    private: void DoReceive()
            {
    			while (checkBox1->Checked == true)
    				{
                Display2^ d = gcnew Display2(this,&Form1::DisplayText1);
                this->Invoke(d);
    			//Thread::Sleep(100);
    			}
            }
    
    private: void DoReceive2()
            {
    			while (checkBox1->Checked == true)
    				{
                Display3^ k = gcnew Display3(this,&Form1::DisplayText2);
                this->Invoke(k);
    			//Thread::Sleep(100);
    			}
            }

    private: void DisplayText1()
            {
    掃描器的code..
    		}
    
    private: void DisplayText2()
            {
    影像的code..
    		}
    我在獨立處理影像的執行緒裡所有for迴圈加了Sleep(0);
    然後整個都變很慢,所以我就沒有這樣做了
    恩...
    2014年11月20日 上午 06:48
  • 調整DoReceive和DoReceive2裡的while迴圈的Sleep時間試試...
    2014年11月20日 上午 07:05
  • Joe Hung 大
    我調整了,結果還是一樣
    我想問說我這樣用多執行緒的方法是正確的嗎?@@

    還是他本來如果執行緒變多的話,一定會被拉慢..
    2014年11月20日 上午 07:40
  • Stopwatch 類別

    http://msdn.microsoft.com/zh-tw/library/system.diagnostics.stopwatch%28v=vs.110%29.aspx

    您可以用Stopwatch去計算執行緒的運算時間,影像處理的時間,可能超過1秒,掃描器可能接近1秒,就算兩個獨立跑執行緒,可能還是會互相拖慢速度...

    2014年11月20日 上午 07:47
  • Joe Hung大

    分別獨立執行的話,為什麼還會互相拖到速度呢?
    因為單一執行很正常,再去執行掃描器的部分就變慢,想請教這是什麼原因@@?


    剛剛有試著把cvWaitKey(10);
    opencv裡的delay給註解,發現掃描顯示的部分就會卡死

    影像就會執行的很流暢
    所以sleep 跟 cvWaitKey(10);是有相同的效果?

    private: void DoReceive()
            {
    			while (checkBox1->Checked == true)
    				{
                Display2^ d = gcnew Display2(this,&Form1::DisplayText1);
                this->Invoke(d);
    			Thread::Sleep(10);
    			}
            }

    2014年11月20日 上午 08:01
  • Application::DoEvents 方法

    http://msdn.microsoft.com/zh-tw/library/system.windows.forms.application.doevents%28v=vs.110%29.aspx

    Thread::Sleep 方法 (Int32)

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

    OpenCV cvWaitKey(int delay)的使用方法

    http://www.pupuliao.info/2014/02/opencv-cvwaitkeyint-delay%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95/

    用.NET展現多核威力(2A) - 一核一緒補充包

    http://blog.darkthread.net/post-2010-01-20-multicore-2a.aspx

    作業系統會切換執行緒,如果沒有Sleep暫停執行緒,或執行緒運算耗時很久,都會影響到其它的執行緒...

    2014年11月20日 上午 08:28
  • Joe Hung

    我看完你貼的一核一緒的分享文了

    有大致上的了解,所以就算是獨立兩個執行緒在動作

    還是要透過切換的方式來進行

    我剛有做一個實驗,我先執行影像的部分,執行的很流暢

    我接著執行掃描器時,影像就慢了下來,但在這邊掃描器的速度是很流暢的

    也就是說會有這樣的現象是很正常的?

    所以不完全是我程式的問題,是跟CPU有關嗎?


    2014年11月20日 上午 09:04
  • 所以才要您用Stopwatch去算影像和掃描器的執行時間,您可以做兩個實驗,

    一、只用一個執行緒,把影像和掃描器的程式都放在裡面。

    二、影像和掃描器分成兩個執行緒,在四核或八核的電腦上跑...

    2014年11月20日 上午 09:27
  • Joe Hung 大
    我實驗做了,如果說要改善現在的狀況
    只能用四核的PC去跑才能改善了嗎?
    2014年11月21日 上午 02:52
  • 不曉得影像和掃描器每秒需要更新幾次?不知道您所謂頓是有多慢?如果無法改善您程式的效能,那就只好增加電腦的硬體效能試試...
    2014年11月21日 上午 03:22
  • Joe Hung 大
    更新幾次是?直接用一個變數當記數嗎?

    會說變慢就是很明顯變慢了,我測試只有單純執行影像的時候,是30ms左右

    但一執行掃描器就慢了下來,變成100ms左右,等於慢了三倍

    但就是不至於到很頓,比較讓我不解的是..我再serial port那讀資料,也是用委派的方式在更新UI的介面

    但執行影像跟serial port讀資料就不會變慢...

    昨天我用T100A,小平板測試 因為身邊只有他是四核心的 @@

    還是一樣慢,而且感覺變更慢XD....這....?
    2014年11月22日 上午 01:55
  • 一、更新幾次的用意,是您對慢的底限可以接受到什麼程度?

    二、serialPort是用事件觸發,只接收幾個字元,顯示在文字盒,處理速度很快。

    三、請不要把平板Atom和桌上型多核心處理器效能相提並論...

    • 已標示為解答 Ervin6285 2014年11月22日 上午 03:41
    2014年11月22日 上午 02:43
  • Joe Hung 大

    喔喔,我知道平板跟桌上型不能比較,但是桌上型在處理時就變慢了,我才會對這提出疑問~

    關於多執行緒跟電腦核心的分工處理速度,我另開討論好了
    這裡先結案
    謝謝您的指導,讓我受益良多 :) 
    2014年11月22日 上午 03:41