熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

c#中使用多線程訪問winform中控件

2013-11-13 09:52:35  來源: .NET編程 

  我們在做winform應用的時候大部分情況下都會碰到使用多線程控制界面上控件信息的問題然而我們並不能用傳統方法來做這個問題下面我將詳細的介紹

  首先來看傳統方法

  public partial class Form : Form
    {
        public Form()
        {
            InitializeComponent();
        }
        private void Form_Load(object sender EventArgs e)
        {
            Thread thread = new Thread(ThreadFuntion);
            threadIsBackground = true;
            threadStart();
        }
        private void ThreadFuntion()
        {
            while (true)
            {
                thistextBoxText = DateTimeNowToString();
                ThreadSleep();
            }
        }
    }

  運行這段代碼我們會看到系統拋出一個異常Crossthread operation not valid:Control textBox accessed from a thread other than the thread it was created on 這是因 以後加強了安全機制不允許在winform中直接跨線程訪問控件的屬性那麼怎麼解決這個問題呢下面提供幾種方案

  第一種方案我們在Form_Load()方法中加一句代碼

  private void Form_Load(object sender EventArgs e)
      {
            ControlCheckForIllegalCrossThreadCalls = false;
            Thread thread = new Thread(ThreadFuntion);
            threadIsBackground = true;
            threadStart();
        }
      加入這句代碼以後發現程序可以正常運行了這句代碼就是說在這個類中我們不檢查跨線程的調用是否合法(如果沒有加這句話運行也沒有異常那麼說明系統以及默認的采用了不檢查的方式)然而這種方法不可取我們查看CheckForIllegalCrossThreadCalls 這個屬性的定義就會發現它是一個static的也就是說無論我們在項目的什麼地方修改了這個值他就會在全局起作用而且像這種跨線程訪問是否存在異常我們通常都會去檢查如果項目中其他人修改了這個屬性那麼我們的方案就失敗了我們要采取另外的方案

  下面來看第二種方案就是使用delegate和invoke來從其他線程中控制控件信息網上有很多人寫了這種控制方式然而我看了很多這種帖子表明上看來是沒有什麼問題的但是實際上並沒有解決這個問題首先來看網絡上的那種不完善的方式

  public partial class Form : Form
    {
        private delegate void FlushClient();//代理
        public Form()
        {
            InitializeComponent();
        }
        private void Form_Load(object sender EventArgs e)
        {
            Thread thread = new Thread(CrossThreadFlush);

  threadIsBackground=true;
            threadStart();
        }

  private void CrossThreadFlush()
        {
            //將代理綁定到方法
            FlushClient fc = new FlushClient(ThreadFuntion);
            thisBeginInvoke(fc);//調用代理
        }
        private void ThreadFuntion()
        {
            while (true)
            {
                thistextBoxText = DateTimeNowToString();
                ThreadSleep();
            }
        }
    }

  使用這種方式我們可以看到跨線程訪問的異常沒有了但是新問題出現了界面沒有響應了為什麼會出現這個問題我們只是讓新開的線程無限循環刷新理論上應該不會對主線程產生影響的

  其實不然這種方式其實相當於把這個新開的線程注入到了主控制線程中它取得了主線程的控制只要這個線程不返回那麼主線程將永遠都無法響應就算新開的線程中不使用無限循環使可以返回了這種方式的使用多線程也失去了它本來的意義

  現在來讓我們看看推薦的解決方案

  public partial class Form : Form
    {
        private delegate void FlushClient();//代理
        public Form()
        {
            InitializeComponent();
        }
        private void Form_Load(object sender EventArgs e)
        {
            Thread thread = new Thread(CrossThreadFlush);
            threadIsBackground = true;
            threadStart();
        }

  private void CrossThreadFlush()
        {
            while (true)
            {
                //將sleep和無限循環放在等待異步的外面
                ThreadSleep();
                ThreadFunction();
            }
        }
        private void ThreadFunction()
        {
            if (thistextBoxInvokeRequired)//等待異步
            {
                FlushClient fc = new FlushClient(ThreadFunction);
                thisInvoke(fc);//通過代理調用刷新方法
            }
            else
            {
                thistextBoxText = DateTimeNowToString();
            }
        }
    }

  運行上述代碼我們可以看到問題已經被解決了通過等待異步我們就不會總是持有主線程的控制這樣就可以在不發生跨線程調用異常的情況下完成多線程對winform多線程控件的控制了


From:http://tw.wingwit.com/Article/program/net/201311/11832.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.