多線程、委託、Invoke解決winform界面卡死的問題,並帶開關(轉載)

一、知識點介紹

1,更新控件的內容,應該調用控件的Invoke方法。

Invoke指: 在擁有控件的基礎窗口句柄的線程上,用指定的參數列表執行指定委託。該方法接收一個委託類型和委託的參數,因此需要定義委託類型變量,然後傳遞給Invoke方法。

 

如果其他線程直接調用方法更新控件內容,報錯:線程間操作無效: 從不是創建控件“richTextBox1”的線程訪問它。

 

2,委託的本質是某一類型的方法,這些方法具有相同的參數和返回類型。

委託類似於C語言中的函數指針,可以指向多個相同類型的函數。

定義委託,只需要在函數返回類型前加上delegate關鍵詞,把函數體大括號{}的內容換成分號即可。比如:

public delegate void DelegateFun(string msg);

DelegateFun就代表了一個函數類型,它接收string參數,返回void。

 

3,開闢一個線程,直接啓動,後面通過掛起和喚醒實現暫停功能。

Thread t = new Thread(Run);

t.Start(); // 啓動

通過判斷線程狀態,決定是否喚醒線程。

if (t.ThreadState == ThreadState.Suspended) // 如果被掛起了,就喚醒

{

t.Resume();

}

暫停就掛起線程:

t.Suspend(); // 停止,掛起線程

 

注:也可以定義一個開關,用來控制開始和結束,在開關爲false的時候,直接continue,這樣表現爲暫停輸出,但是實際上線程一直在運行。

 

二、界面和代碼

 


複製代碼

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication3
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 因爲控件的Invoke方法需要接收委託變量,因此需要定義委託和委託變量
        /// 定義一個委託,接收一個參數
        /// </summary>
        /// <param name="msg"></param>
        public delegate void DelegateFun(string msg);
        /// <summary>
        /// 定義一個委託變量
        /// 這個委託變量,需要初始化指定具體的方法;然後傳遞給控件的Invoke方法調用。
        /// </summary>
        public DelegateFun Fun1;


        /// <summary>
        /// 定義一個線程,處理數據,並更新界面
        /// </summary>
        private Thread t = null;
        // 開始按鈕
        private void button1_Click(object sender, EventArgs e)
        {
            this.Invoke(Fun1, "開始...");          
          
            // 增加判斷,避免每次單擊都開闢一個線程
            if (t == null)
            {
                t = new Thread(Run);
                t.Start();   
            }
            if (t.ThreadState == ThreadState.Suspended) // 如果被掛起了,就喚醒
            {
                t.Resume();
            }
               
        }
        // 結束執行
        private void button2_Click(object sender, EventArgs e)
        {           
            t.Suspend(); // 停止,掛起線程
            this.Invoke(Fun1, "...停止");
        }
        
        // 具體做事情的方法
        public void Run()
        {
            //...... 處理一些事情,然後輸出日誌
            int i = 0;           
            while (true)
            {               
                i++;
                // this指Form2
                //Invoke指: 在擁有控件的基礎窗口句柄的線程上,用指定的參數列表執行指定委託。
                //Invoke的參數是一個委託類型,因此必須定義委託變量 
                this.Invoke(Fun1, i.ToString());               
            }
        }

        //在form初始化的時候,給委託變量賦值具體的方法
        private void Form2_Load(object sender, EventArgs e)
        {
            //給委託變量初始化具體的執行方法
            Fun1 = Print;
        }

        // 輸出日誌的方法
        public void Print(string msg)
        {
            // 新開闢的線程,不能直接調用這個方法。原因是控件只能由創建它的線程調用。
            // 其他線程調用提示錯誤: 線程間操作無效: 從不是創建控件“richTextBox1”的線程訪問它。
            this.richTextBox1.AppendText(msg + "\r\n");
            this.richTextBox1.ScrollToCaret();
        }

    }
}

複製代碼

 

轉載地址:https://www.cnblogs.com/wang7/p/5869399.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章