【C#】【Thread】SynchronizationContext線程間同步


  SynchronizationContext在通訊中充當傳輸者的角色,實現功能就是一個線程和另外一個線程的通訊。

  需要注意的是,不是每個線程都附加SynchronizationContext這個對象,只有UI線程是一直擁有的。故獲取SynchronizationContext也只能在UI線程上進行SynchronizationContext context = SynchronizationContext.Current;

  那什么時候會用到呢?

  在多線程操作時往往需要切回某個線程中去工作,等完成后再切回來。

  如主UI線程中創建了一個子線程A。A中添加了委托事件。UI線程中向A線程的類注冊了事件,當A線程觸發事件時去修改UI上的屬性如TEXT。

  這個時候往往要在UI線程向子線程注冊的事件方法中使用控件的invoke方法才能訪問UI線程中的控件,因為這些注冊的事件(委托)方法代碼雖然看似寫在UI線程的Form類中,但實際上是注冊在了子線程A的事件中,它們是會被子線程A觸發事件時在子線程內部執行的。這樣,我們不得不在主UI線程的類的注冊事件方法中通過控件的Invoke方法才能訪問控件,這樣做十分麻煩。我們想和系統的控件事件一樣,直接在注冊的事件方法中訪問控件。那么這個時候就可以用SynchronizationContext了。

  SynchronizationContext.Send(SendOrPostCallback d,object state);

  SynchronizationContext.Post(SendOrPostCallback d,object state);

  d 為一個沒有返回值,並且具有一個Object類型傳入參數的委托(SendOrPostCallback );

  state 為執行這個委托時的參數(object);

注意:

  SynchronizationContext的對象不是所有線程都被附加的,只有UI主線程會被附加。

  對於UI線程來說,是如何將SynchronizationContext這個對象附加到線程上的呢?

  在Form1 form = new Form1()之前,SynchronizationContext對象是為空,而當實例化Form1窗體后,SynchronizationContext對象就被附加到這個線程上了。

  所以可以得出答案了:當Control對象被創建的同時,SynchronizationContext對象也會被創建並附加到線程上。所以在使用時,一定要等窗體InitializeComponent(); 這個完成后 它才能得到一個不是NULL的對象.

  

  那么SynchronizationContext的Send()和Post()二個方法有什么區別呢?

  Send() 是簡單的在當前線程上去調用委托來實現(同步調用)。也就是在子線程上直接調用UI線程執行,等UI線程執行完成后子線程才繼續執行。

  Post() 是在線程池上去調用委托來實現(異步調用)。這是子線程會從線程池中找一個線程去調UI線程,子線程不等待UI線程的完成而直接執行自己下面的代碼。

  

例子:

        /// <summary>
        /// 這里需要在主線程里定義,
        /// 並在主線程獲得context = SynchronizationContext.Current
        /// </summary>
        private SynchronizationContext context;
        /// <summary>
        /// 窗體加載
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            //此處就是之前提的在主線程獲得SynchronizationContext
            context = SynchronizationContext.Current;
            //之后可以開線程了
            Thread thread = new Thread(new ThreadStart(Start));
            thread.IsBackground = true;
            thread.Start();
        }
        /// <summary>
        /// 線程操作
        /// </summary>
        private void Start()
        { 
            for(int i=0;i<100;++i)
            {
                //這邊即可正常調用主界面的控件了
                context.Send(operation, i);//正確
                //按原先直接應用,因為使用到控件會報錯
                operation(i);//報錯
                Thread.Sleep(100);
            }
        }
        /// <summary>
        /// 線程操作
        /// </summary>
        /// <param name="obj"></param>
        private void operation(object obj)
        {
            textBox1.AppendText(obj.ToString() + "\r\n"); 
        }

 

參考文章:http://blog.csdn.net/iloli/article/details/16859605

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM