C# 訊息 介面卡頓 介面程序 工作程序

firespeed發表於2024-10-26

一 訊息

訊息與訊息迴圈,是所有的GUI開發裡共同的概念:
訊息Message,有的地方也叫事件;
① 滑鼠訊息;
② 鍵盤訊息;
③ 繪製事件;
④ 視窗最大化、最小化;

1 訊息迴圈

訊息迴圈,Message Loop所有的介面訊息,都是一個while迴圈裡處理的用虛擬碼表示:

List<Message>msgList=new List<Message>()
while(message=GetMessage())
{
依次處理message..
}

真實的訊息迴圈

Application.Run(new Form1());

具體的訊息處理過程:

protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
}

所有的介面事件回撥,本質上都執行在訊息迴圈裡,在訊息迴圈裡,作進一步的分發處理。
比如,一個Message是滑鼠事件,則分發給相應的空間處理。

void button1_MouseUp(object sender,MouseEventArgs e)
{
}

執行這個訊息迴圈的執行緒,就是介面執行緒,在WinForm裡,主執行緒即介面執行緒。

static void Main()
{
Application.Run(new Form1());
}

三 介面卡頓

按鈕處理程式需要9秒完成;在這個9秒內整個介面是卡主的,不可操作的,為什麼?
訊息迴圈:每一個訊息處理都要儘快完成

while(message=GetMessage())
{
switch(訊息型別)
case 滑鼠訊息;
case 鍵盤訊息;
}

所有的訊息處理回撥,都喲啊儘快返回。當處理時間太長時,介面會有卡頓之感(大於300毫秒左右)

四 工作執行緒

思考:
如果確實需要處理一件耗時較長的工作…
例如,查詢資料庫,上傳下載,編解碼…都可能需要較長時間才能完成。怎麼解決?

工作執行緒Work Thread
如果事件處理需要較長時間,應當建立一個執行緒來處理這個任務。此執行緒稱為“工作執行緒”。
由於button1_Click()會立即返回,不會引起介面卡頓。

介面執行緒:一直執行,處理介面事件;
工作執行緒:工作完成後退出;
回顧執行緒的特點:獨立,並行;

五 介面的更新

錯誤的實現:

1 建立工作執行緒;

2 在工作執行緒中直接更新TextBox的顯示;

觀察:執行程式,程式會有崩潰提示。
為什麼不能在工作執行緒中直接訪問textBox1呢?
在工作執行緒中訪問UI控制元件時,需使用Invoke方法

Control.Invoke(method,args)

當呼叫Invoke時,實際上推送了一個自定義的訊息到訊息迴圈中。當訊息被處理時,相應的回撥被執行。

正確的實現:
① 定義一個委託型別myCallback;
② 定義一個回撥處理ShowProgress;
③ 使用Invoke推送一個自定義事件到訊息迴圈;
注意:Invoke訊息的回撥也是在介面執行緒中執行的;

第一原則:介面問題的處理不能太久,否則卡頓;
第二原則:當任務時間較長時,則建立工作執行緒;
第三原則:在工作執行緒中不可以直接更新UI,需藉助Invoke來傳送一個自定義的訊息;

六 Action與Func

委託,實際上是對一類方法的特徵描述;
例如:

public delegate void selfCallback(string str);

表示的是"引數為striing、返回值為void"的方法;

兩個通用的Delegate:
System.Action表示返回值為void的方法;
System.Func 表示返回值不是void的方法;
幾乎所有的方法,都可以用這兩種委託來表示。
例如:

void test1(string a,int b);

由於返回值是void型別,可以用Action表示:

new Action<string,int>(this.test1);

例如:

Student test2(string a,int b)

由於返回值不是void,可以用Func表示

new Func<string,int,Student>(this.test2);

在工作執行緒裡更新UI時,直接使用Action/Func即可,不需要專門定義一個Delegate

this.Invoke(new Action<string>(this.ShowProgress))
public void ShowProgress(string text)
{
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Action與Func
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
Thread th = new Thread(new ThreadStart(this.Execute));
th.Start();
}

private void Execute()
{
//此回撥處理需要3秒才能完成
this.Invoke(new Action<string>(this.ShowProgress), "3..\r\n");
Thread.Sleep(1000);

this.Invoke(new Action<string>(this.ShowProgress), "2..\r\n");
Thread.Sleep(1000);

this.Invoke(new Action<string>(this.ShowProgress), "1..\r\n");
Thread.Sleep(1000);

this.Invoke(new Action<string>(this.ShowProgress), "OK..\r\n");
}

public void ShowProgress(string text)
{
//這個方法是在訊息迴圈(介面執行緒)裡
textBox1.AppendText(text);
}
}
}

七 InvokeRequired

Control.InvokeRequired用來判斷是不是在工作執行緒

if(this.InvokeRequired)
{
//判斷當前執行緒是不是工作執行緒
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

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

private void button1_Click(object sender, EventArgs e)
{
Thread th = new Thread(new ThreadStart(this.Execute));
th.Start();
}

private void Execute()
{
ShowProgress("3\r\n");
Thread.Sleep(1000);

ShowProgress("2\r\n");
Thread.Sleep(1000);

ShowProgress("1\r\n");
Thread.Sleep(1000);
ShowProgress("OK\r\n");
}
/// <summary>
/// 此方法既可以在工作執行緒中呼叫、又可以在介面執行緒中呼叫
/// </summary>
/// <param name="str"></param>
public void ShowProgress(string str)
{
if(this.InvokeRequired)
{
//從工作執行緒中呼叫
Console.Write("Call In Work Thread:" + str);
this.Invoke(new Action<string>(this.ShowProgress), str);
}
else
{
//從介面執行緒中呼叫
Console.WriteLine("Call in Message Loop:" + str);
textBox1.AppendText(str);
}
}
}
}

出處:https://mp.weixin.qq.com/s/Uo0Ye-24uNpMJKtgzm2X8A

相關文章