2015年11月2日 星期一

跨執行&委派

最近在寫執行緒,參考了許多文章,這是篇簡單又實用的文章,
轉貼出處http://toyo0103.blogspot.tw/2012/03/c-ui.html

作者說的很清楚,執行緒是拿來做重覆、繁雜的事,將資源留給
主程式去運作,執行緒執行到定點時定時回報
但是他忘了說,要記得thread作完它的工作後,要記得釋放資源,
不要被它lock住了,而讓程式變殭屍。
因為我們用的是C#,我的老大最愛說的一句話是『不要相信c#的
自動資源釋放,它是在資源不夠的時候,才會回收
資源。」總歸一句話,出來混總是要還的;所以寫自已寫的程式,
資源要自己收。


寫了四年的程式,第一次寫執行緒,第一次使用委派,真的是老菜鳥。
在執行緒中要更新win form 頁面,結果 GG 了,這是什麼???
跨執行緒作業無效 存取控制項 'textbox1' 時所使用的執行緒與建立控制
項的執行緒不同。
原來這是多執行緒的天條,不同的執行緒,不可以跨界去使用別人的UI。
我的解決方法,在拜完G神之後還是有看沒懂

多數人都是轉載這篇

如何跨執行緒存取UI
當我試著用WinFrom寫多執行緒時,卻出現了以下錯誤訊息
跨執行緒作業無效: 存取控制項 'textBox1' 時所使用的執行緒與建立控制項的執行緒不同。
詢問高手後有三種方法解決:
1.Form.CheckForIllegalCrossThreadCalls = False
2.建立委派
第一種方法,據說不安全,但我也不曉得哪裡不安全,但用起來還蠻方便的。
第二種方法,比較正統使用委派的方式,若爾後需要改任何控制項的文字時(需有text屬性的),呼叫 myU即可。


private delegate void myUICallBack(string myStr, Control ctl);
  private void myUI(string myStr, Control ctl)
  {
  if (this.InvokeRequired)
  {
  myUICallBack myUpdate = new myUICallBack(myUI);
  this.Invoke(myUpdate, myStr, ctl);
  }
  else
  {
  ctl.Text = myStr;
  }
 

這實在是小的愚昧,我認識它,它也認識我,只是我讀不懂它。
只能硬用了。

最近有點知道委派是怎麼回事了,若是有錯,請各位先進,不吝指教。
原來委派有2種方式,一種是使用事件方式,在註冊事件後就可以呼叫它
使用它了,可以參考余小章 @ 大內殿堂的文章
http://www.dotblogs.com.tw/yc421206/archive/2009/02/16/7206.aspx

另一種就是http://toyo0103.blogspot.tw/2012/03/c-ui.html中的委派,
直接呼叫該物件的function,介由this.InvokeRequired判斷是否是同一個thread
若不是,則呼叫委派,由該物件重新執行function,我想大約也是讓你有聽沒有懂;
你可以依步驟這樣做。

1.在要顯示UI的form中,寫好你要執行的動作;如:

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

        public void ReflashPanelPage(List<Release> Rlist)
        {
            TextBox txtbox = new TextBox();

            txtbox = (TextBox)Utility.ControlFind(this.pl_Main, "txt_descript");
           int Row =Rlist.count()-1;
            foreach (Release Item in Rlist)
            {
                //輪到的 Panel變色
                Panel Pl = (Panel)Utility.ControlFind(this.pl_Main, Item.Name);

                Pl.BackColor = System.Drawing.Color.LightYellow;
                Pl.Size = new System.Drawing.Size(192, Pl.Size.Height);
                Pl.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;

                txtbox.Text = null;
                txtbox.Visible = true;
                txtbox.Text += "Version";
                txtbox.Text += Item.Version + Environment.NewLine;
                txtbox.Text += Item.Description;


                if (Row != 1)
                {
                    Pl.BackColor = System.Drawing.Color.Transparent;
                    Pl.Size = new System.Drawing.Size(180, Pl.Size.Height);
                    Pl.BorderStyle = System.Windows.Forms.BorderStyle.None;
                }
               Row--;

            }
        }
    }

我要傳入List<Release>,輪到的 Panel變色,
但是呼叫它的另一個物件中的thread

2.
 public class Updater : IDisposable { public Form1 F1 = new Form1(); public Updater() { ParameterizedThreadStart updaterThread = new ParameterizedThreadStart(UpdateProcess); Thread ThreadObj = new Thread(updaterThread); ThreadObj.Start(); } public void UpdateProcess() { List<Release> ReleasList = getReleaseItem(); F1.ReflashPanelPage(ReleasList); //在這裡由update 呼叫它 } }

這時候程跑到這一定會出錯的 跨執行緒作業無效 存取控制項 '‧‧‧‧‧' 時所使用的執行緒與建立控制項的執行緒不同。因為不能執行緒來更新UI,這是執行緒的天條,不可侵犯。
所以我們做第3步

3.在form1中建立delegate ,並使用它

  public partial class Form1 : Form
    {

   private delegate void ReflashUpdateUI(List<Release> Update);
        public Form1()
        {
            InitializeComponent();
        }
.....
.....
.....

 public void UpdateUIChange(List<Release> Update)
        {
            //判斷物件是否在同一個執行緒上
            if (this.InvokeRequired)
            {
                //當InvokeRequired為true時,表示在不同的執行緒上,所以進行委派的動作!!
                ReflashUpdateUI UI = new ReflashUpdateUI(UpdateUIChange);
                this.Invoke(UI, Update);
            }
            else
            {
                ReflashPanelPage(Update);
            }

        }

}

5.修改thread呼叫 delegate 來執行UI 更新的功能

   public void UpdateProcess() { List<Release> ReleasList = getReleaseItem(); F1.UpdateUIChange(ReleasList); //在這裡由update 呼叫它 }


以上完成

第二種方式,是我比較常用的委派,我覺得它比較簡單,一樣在form 中寫好要執行變更UI 的動作後,宣告好委派後,再寫好觸動委派的function  ex :UpdateProcess()
,再在thread 中呼叫 UpdateProcess,由InvokeRequired來判斷是否是同一個執行緒,若是不同則執行委派。



參考出處:http://www.dotblogs.com.tw/shinli/archive/2015/04/16/151076.aspx
參考出處 http://www.dotblogs.com.tw/yc421206/archive/2009/02/13/7141.aspx
參考出處 http://code2study.blogspot.tw/2011/09/c.html

沒有留言:

張貼留言