none
backgroundWorker多執行緒不能使用控制項? RRS feed

  • 問題

  • WinForm中使用backgroundWorker來進行多工
    但是執行緒好像沒辦法使用視窗上的控制項
    比如改變視窗中的Label的Text就的出現"跨執行緒作業無效: 存取控制項 'label1' 時所使用的執行緒與建立控制項的執行緒不同。"的錯誤....

    <textarea name="code" class="c#">
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;

    using System.Net;
    using System.Net.Sockets;

    namespace ClientServer
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            private void Form1_Load(object sender, EventArgs e)
            {
                backgroundWorker1.RunWorkerAsync();
            }

            private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                try
                {
                    Socket theConnection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                   
                    IPAddress myIP = IPAddress.Parse("172.18.50.1");
                    IPEndPoint myEP = new IPEndPoint(myIP, 9999);
                    TcpListener myListener = new TcpListener(myEP);

                    myListener.Start();

                    theConnection = myListener.AcceptSocket();

                    int bytes = 0;
                    byte[] recv = new Byte[256];
                    string recvstring = "";
                    bytes = theConnection.Receive(recv, recv.Length, 0);
                    recvstring = Encoding.UTF8.GetString(recv, 0, bytes);
                    label1.Text = recvstring;

                    theConnection.Shutdown(SocketShutdown.Both);
                    theConnection.Close();
                }
                catch (Exception error)
                {
                    MessageBox.Show(error.ToString());
                }
            }
        }
    }
    </textarea>

    2007年4月9日 上午 01:52

解答

  • 若你要控制使用者介面,你必須要使用 ReportProgress() 來回報資料,然後在 BackgroundWorker 的 ProgressChanged 事件來更新使用者介面才行。
    2007年4月9日 上午 02:11
    版主

所有回覆

  • 若你要控制使用者介面,你必須要使用 ReportProgress() 來回報資料,然後在 BackgroundWorker 的 ProgressChanged 事件來更新使用者介面才行。
    2007年4月9日 上午 02:11
    版主
  • Dear 小朱:

    1.我用了你的方法後可是WindowForm的UI畫面變成無法操作了....?要等到接到訊息後才會解開


    2.我要怎麼當Server Send訊息過來後可以一直改變Label內容呢?因為我是把backgroundWorker1.RunWorkerAsync();寫在Form_Load裡面,可是這樣的話他就只會作一次就不會再繼續了..那有沒有其他的方法可以用呢?

    2007年4月9日 上午 03:13
  • 1. 不知道你程式怎麼寫的,但我用過是可以的。

     

    2. RunWorkerAsync() 會在 DoWork 中的程式作完後就會結束,所以你必須要有一個方法知道有資料進來,或者是不要讓 DoWork 結束。

     

     

    2007年4月9日 上午 05:03
    版主
  • 不太清楚你是怎麼寫的,以下是一段sample code FYI:

     

    Code Snippet

    PingReply result;

    private void btn_Scan_Click(object sender, EventArgs e)
     {
                
         backgroundWorker1.RunWorkerAsync();
     
         backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
         backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);

    }

     

     void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
     {
          txt_Result.Text = result.Status + "\r\n" + txt_Result.Text;
          Application.DoEvents();
      }

     void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
      {
          for (int i = 0; i < 101; i++)
          {
              Ping p = new Ping();
              result = p.Send(txt_ip.Text.Trim());
              backgroundWorker1.ReportProgress(i);
              System.Threading.Thread.Sleep(500);
          }
      }

     

     

    2007年4月9日 上午 06:27
  • Dear 小朱:

                      我想請教兩個問題

                       1.跨執行緒作業無效: 存取控制項 'label1' 時所使用的執行緒與建立控制項的執行緒不同。這問題我要是把Label.text寫在backgroundWorker1_ProgressChanged或backgroundWorker1_DoWork的話會出現這個問題..

     

                       2.有關於上面問的問題是您說的不要讓 DoWork 結束...這個要怎麼設呢?

     

    2007年4月9日 上午 11:16
  • 我的用法是這樣:

     

    Code Snippet

          private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                try
                {
                    if (e.UserState != null)
                    {
                        if (e.UserState is TableProgressUpdateInfo)
                        {

                           ...                       

                      }
                        else if (e.UserState is EntireProgressUpdateInfo)
                        {
                            EntireProgressUpdateInfo einfo = e.UserState as EntireProgressUpdateInfo;

                          this.labelDataRowCount.Text = einfo.OverallStepsCount.ToString("###,###,###,##0");
                            this.labelProceedCount.Text = einfo.CompletedStepsCount.ToString("###,###,###,##0");
                        }
                    }

                    Application.DoEvents();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + "\r\n\r\n" + ex.StackTrace);
                }
            }

     

          private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
            {
               this._workflow = new Workflow();
               this._workflow.ConverterProgressUpdate += new Workflow.ConverterProgressUpdateEventHandler(this.Workflow_ConverterProgressUpdate);
               this._workflow.ConverterChange += new Workflow.ConverterChangeEventHandler(this.Workflow_ConverterChange);

             try
                {
                    this._workflow.BeginWorkflow();
                    e.Result = true;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + "\r\n" + ex.StackTrace);
                    e.Result = false;
                }
            }

     

    沒碰到過你的問題,你的 BackgroundWorker.WorkerReportProgress 有沒有設定為 true?

     

    2007年4月9日 上午 11:35
    版主
  • 這好像跟你們說的有點出入,這個問題是你做非同步呼叫時,會建一個背景執行序,等非同步回傳時須把背景執行序的值代到UI值行序的Control在這裡就會出問題,這裡會出現有時可以值接帶到Control,有時該Control 須要做委派動作後在寫一次,反正這是標準問題,MSDN 有非同步教學文件,你去看一看就會了.
    2007年4月10日 上午 12:54
  • 好說 大大: 小弟跟你跪求能給個簡單的或類似範例好讓小的打通腦筋~

                       試了2天還是搞不太懂...

    2007年4月10日 上午 07:35
  • 這一段程式碼即可對TextBox之類的做異動,不知道你的問題是什麼? 而且在執行時,Form還是可以拉著跑.

    註:WorkerReportsProgess要設為True

     

    PingReply result;

    private void btn_Scan_Click(object sender, EventArgs e)
     {
                
         backgroundWorker1.RunWorkerAsync();
     
         backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); 
         backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);

    }

     

     void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
     {
          txt_Result.Text = result.Status + "\r\n" + txt_Result.Text; //把結果放到TextBox.Text內,這個地方不就是你所問的?
          Application.DoEvents(); //讓TextBox的訊息先顯示,不要等全部做完,才顯示
      }

     void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
      {
          for (int i = 0; i < 101; i++)
          {
              Ping p = new Ping();
              result = p.Send(txt_ip.Text.Trim());
              backgroundWorker1.ReportProgress(0); //執行到此行時,會觸發ProgressChanged的事件.
              System.Threading.Thread.Sleep(500);
          }
      }

    2007年4月10日 上午 09:48
  •  參考 http://msdn2.microsoft.com/en-us/library/48cfdff6(vs.80).aspx 有一點長

     

    關鍵在 這些委派

    public delegate void FHandler(double Value, double Calculations);
    public delegate void A2Handler(int Value, double Calculations);
    public delegate void LDHandler(double Calculations, int Count);

     

    跟下面這幾行

    this.BeginInvoke(new FHandler(FactHandler), new Object[]
          {Value, Calculations});


     

    2007年4月11日 上午 12:21
  • 對不起各位..我沒把需求說清楚先再次跟大家說聲對不起..

    1.小弟不懂的問題是我的程式一開始執行時候就開始跑以下程式,不然要是有Server傳送訊息給他時就接收不到了,但是我的問題又來了~要是把下面程式寫在Form_Load裡面UI畫面會停住在myListener.AcceptSocket();這段,所以我才選用backgroundWorker來做,可是我搞不懂的是..要怎樣讓它當Server傳訊息給他後可以一直收到訊息?因為Form_lad是程式一執行就開始跑但是他只會跑一次,那要用什麼方法讓他可以收呢?有點摸不著頭緒...

     

    2.當我收到訊息後可以一直改變textbox內容,有辦法覆蓋掉先前接收的值嗎?之前大大所提的方法我知道怎用了謝謝~

     

     

     

     

    Code Snippet

    try
                {
                    Socket theConnection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    string ip = Dns.GetHostAddresses(Dns.GetHostName())[0].ToString();
                    IPAddress myIP = IPAddress.Parse(ip);
                    IPEndPoint myEP = new IPEndPoint(myIP, 1234);
                    TcpListener myListener = new TcpListener(myEP);
                    myListener.Start();

                   theConnection = myListener.AcceptSocket();

                   int bytes = 0;
                    byte[] recv = new Byte[256];
                    string recvstring = "";
                    bytes = theConnection.Receive(recv, recv.Length, 0);
                    recvstring = Encoding.UTF8.GetString(recv, 0, bytes);
                    label1.Text = recvstring; //.Split('※')[0];
                    //linkLabel1.Text = recvstring.Substring(recvstring.LastIndexOf("※") + 1);

                    theConnection.Shutdown(SocketShutdown.Both);
                    theConnection.Close();
                    myListener.Stop();
                    //theConnection.Disconnect(true);
                }
                catch (Exception error)
                {
                    MessageBox.Show(error.ToString());
                }

     

     

    2007年4月16日 上午 08:53
  • 暈倒了 TcpListener 就已經有BeginAcceptSocket ,EndAcceptSocket ..非同步的樣式了,不用在去寫 BackgroundThread 了
    2007年4月16日 上午 09:11
  • 好說大大..

    1.可以教我怎麼改寫我現在的程式嗎?...

    2.想請教的是我在Server那邊Send給使用者時後(Send訊息給上面所發表過的程式接收),萬一對方是離線狀態的話就會出現Error問題~這有辦法說當他傳給對方時候要是他離線的話就跳過或是忽略..可以的話要怎麼做?..

    2007年4月17日 上午 12:53
  • 你要怎麼功能告訴我,我不能教你寫,我只能指引你方向,我不像上面的大大,這是他們的工作,他們有Coco可以領,我只是小小Coding員,沒那麼多時間解給你,而且這個問題領域很廣.

    而就你原來的問題我原本你是做硬體介面的,當然這樣你才需要用Socket 去做,不然像你要寫P2P,或一些傳輸協定,坦白說不建議自己寫自己自創,中間的問題點很多,像網路中斷,續傳,重覆傳,資料安全...,整體而言風險太大,時間很長而且要很多專業知識,所以你看你要怎麼功能,直接找相關的文件跟Library,如MS 你可以查 MSMQ,Web Service,Remort....或直接學WCF,而Thrid Paten 你可以查 Component Source..去查人家開發的元件就可以了.

    2007年4月17日 上午 01:44
  • 好說大大~真對不起一直打擾你時間~不過我照你用的方式是有寫出來後,我發現我的Client的UI畫面一直在等待接收訊息所以都沒跑出來,等到我把Server訊息送出去後Client畫面跑出來了~但是我在第二次送出訊息後Client的Label的內容並沒有變更??不太懂要怎麼讓他可以一直接收訊息後改變label的內容

    Code Snippet

    public string SocketClinet( string IP, int PortNumber,TcpListener listener)
            {
                string message = "";
                Socket theConnection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPAddress myIP = IPAddress.Parse(IP);
                IPEndPoint myEP = new IPEndPoint(myIP, PortNumber);
                TcpListener myListener = new TcpListener(myEP);

                myListener.Start();

                myListener.BeginAcceptSocket(new AsyncCallback(DoAcceptSocketCallback), listener);

                int bytes = 0;
                byte[] recv = new Byte[256];
                bytes = theConnection.Receive(recv, recv.Length, 0);
                message = Encoding.UTF8.GetString(recv, 0, bytes);
                theConnection.Shutdown(SocketShutdown.Both);
                theConnection.Close();

                return message;
            }

     

            public static ManualResetEvent clientConnected = new ManualResetEvent(false);

     

            public static void DoAcceptSocketCallback(IAsyncResult ar)
            {
                TcpListener listener = (TcpListener)ar.AsyncState;

                
                Socket clientSocket = listener.EndAcceptSocket(ar);

               
                clientConnected.Set();
            }

     

     

    2007年4月17日 上午 02:16
  • 非同步樣式使用錯誤,你去查 "非同步用戶端通訊端範例" 這篇文章.

    再去查一下我上一篇 有關將 BackgroudThread 值帶到 UIThread Control 裡的文章就可以了 

    2007年4月17日 上午 02:30
  • 另外你怎麼測試 Message 有沒有進來,還是UI沒有更新,你把你的測法貼上來,我才知道你的概念正不正確,問題會出現在那裡.
    2007年4月17日 上午 02:46
  • 我們有摳摳拿?? 回答問題是我們的工作?? 您誤會了,我並沒有收到任何摳摳,回答問題只是我個人喜好,出於個人自發性的行為,雖然我不是MVP,但我記得,MVP也一樣是義務的,微軟並不會付摳摳.這在MVP資格說明中有提到.http://www.microsoft.com/taiwan/community/what_is_MVP.htm

     

    只是我目前還是沒有Catch到他的問題所在, 搞不懂到底想要做什麼,而你的答案似乎較為被他所接受,所以加油囉.

    2007年4月17日 上午 02:59
  • 小豆芽:

    我看你的原始問題,似乎和 Windows Form 與執行緒的底層運作有關。建議你先了解 WinForm 背後的執行緒機制。以下文字摘自我之前發表在恆逸資訊的 .NET Magazine 的文章:

     

    在撰寫多執行緒的Windows 表單應用程式時,有一項必須特別注意的規則:只有在建立該表單(或控制項)的執行緒中,才能存取、修改表單(或控制項)的內容。如果讀者到MSDN Library查看Control.BeginInvoke方法的說明,也可以得到類似的訊息(雖然不是很明顯),其中有一段備註是這麼說的:「在任何執行緒裡面可以安全呼叫的函式只有四個:InvokeBeginInvokeEndInvoke、和 CreateGraphics。」進一步的解釋這句話,它的意思就是:除了上述四個方法,您在任何執行緒當中存取或修改Control類別的屬性或方法時,都有可能導致程式發生不預期的錯誤,例如:畫面沒有正常更新、程式當掉等等。而由於Form類別也是繼承自Control類別,因此,就如本文一開始所說的,如果您想要存取表單或控制項的屬性或方法,這些程式碼一定要執行於建立該表單或控制項的執行緒當中,通常這個執行緒就是應用程式預設的執行緒,又稱為主執行緒。

     

    為什麼會有這項規則?簡單的說,是因為.NET控制項在設計時就是執行緒相依的(thread affnity),因此您只有在建立該表單或控制項的執行緒當中,才能安全地存取其屬性。但這樣的說明仍不足以讓我們了解背後真正的原因,在本文中,筆者將會詳細說明這項規則的來龍去脈,如果不遵守這個規則會造成什麼後果,並且示範正確的程式撰寫方式。

     

    若覺得可能有幫助,可到 http://www.netmag.com.tw/default.aspx 下載全文。直接下載:http://www.netmag.com.tw/member/netmag_article/N051204702.pdf(需成為會員方可下載)

    2007年4月17日 上午 03:30
  • 關於委派的範例程式碼,如果不嫌棄的話,這邊有 VB2005 的觀念與範例:

    [VB2005]工作緒變更畫面控制項的值

    http://tlcheng.spaces.live.com/blog/cns!145419920BFD55A7!1440.entry

     

    另外這是在 vb 討論區討論一個封包擷取器的程式碼變更範例,剛好就是 tcpListener:

    http://forums.microsoft.com/MSDN-CHT/ShowPost.aspx?PostID=1024049&SiteID=14

    2007年4月17日 上午 09:01
  • MVP 也沒有義務回覆,MVP 只是出於自願參與論壇相關討論。義務看起來像是責任的一種,事實上並沒有這種義務。

     

    所以 MVP 會參與的人會參加,講師的就比較少參加,微軟也沒有強制 MVP 或微軟員工參與論壇。

     

    論壇是由微軟官方建構,提供網友彼此互相切磋的地方,微軟正式技術服務管道是收錢的,請上 http://support.microsoft.com/ 查詢。

     

    微軟鼓勵員工、MVP、及使用者參與論壇交換心得,雖然沒有 Coco ,不過積極參與論壇的人,可能會由負責的 Moli 去找經費或是其他資源贈送給當月參與最多的幾個人,大部分是送原文書一本,你可以到公告區去參考看看過去數個月送的書。這屬於獎勵性質,沒有對價關係,沒有說你回應幾篇送一本或是送你一本必須回應幾篇。

     

    我自己是念水的,主要工作內容是水利署、氣象局這邊的研究計畫,是自己養自己。我的資訊在個人網站有公開,直接點選暱稱裡面有個人網站的位置。

    2007年4月17日 上午 09:20
  •  好說 寫信:

    你要怎麼功能告訴我,我不能教你寫,我只能指引你方向,我不像上面的大大,這是他們的工作,他們有Coco可以領,我只是小小Coding員,沒那麼多時間解給你,而且這個問題領域很廣.

     

    請舉個我們有收 COCO 的例子吧。

    沒有的話就不要亂扣人家帽子,我們自己都已經夠窮了還要被扣帽子,換作是你你做何感想?

    2007年4月17日 上午 11:53
    版主
  • 對不起,這是我對MVP的誤知,雖然我沒有惡意,對還是對被受影響的人深深一鞠躬.
    2007年4月18日 上午 12:22
  • 不好意思題外話一下
    轉貼一下小朱大的一篇文章
    http://www.dotblogs.com.tw/regionbbs/archive/2009/05/03/mvp.aspx
    看過後應該對MVP會有進一步的認識

    ^_^


    topcat(姍舞之間的極度凝聚)http://www.dotblogs.com.tw/topcat/
    2009年8月18日 上午 04:03
  • ... 這篇已經過期很久了,不用特別挖出來貼啦~
    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    2009年8月18日 上午 05:45