欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#?winform?窗體控件跨線程訪問的實現(xiàn)

 更新時間:2023年12月14日 09:47:24   作者:Big?Clever.  
在做winform開發(fā)時,如果在子線程中去設置主線程中UI控件的屬性,會出現(xiàn)“跨線程調(diào)用異?!?本文就來介紹一下C#?winform?窗體控件跨線程訪問的實現(xiàn),感興趣的可以了解一下

在寫計算機網(wǎng)絡課設的時候,遇到一個需求:建立的服務器需要一直監(jiān)聽來自某個端口的連接請求。一開始看到我想這不是很簡單嗎,正好剛剛學習了線程有關的知識,直接開個新線程掛在后臺執(zhí)行不就行了,抱著這樣簡單的想法,一堆錯誤接踵而至。

首先出現(xiàn)的問題就是窗體控件的跨線程訪問,會出現(xiàn)這樣的一個報錯信息:System.InvalidOperationException:“線程間操作無效: 從不是創(chuàng)建控件“textBox1”的線程訪問它。”

去找了找解決方案,其中最簡單的是這樣的一行代碼:

Control.CheckForIllegalCrossThreadCalls = false;

這行代碼只需要加到需要進行控制的控件那里,就會讓程序忽略線程的危險操作。

這樣確實就解決了問題,但是這是一個全局的靜態(tài)變量,如果在其他地方不小心修改了這一變量,每個控件就又會崩掉。所以,正統(tǒng)的解決方法應該是使用委托。

什么是委托?C#的委托類似于C++的函數(shù)指針,是一個可以批量執(zhí)行函數(shù)的容器。我們知道這個問題是發(fā)生在跨線程訪問中,那么我們?nèi)绻尯瘮?shù)調(diào)用發(fā)生在創(chuàng)建這個控件的線程中,那么就不是跨線程訪問,也就不存在這個問題了,因此我們使用this.invoke(delegrate)來使用委托。

來解釋這種解決方法,我們設想一個簡單的情景:我們需要在一個文本框中實時的更新當前時間,我們希望同時還可以在窗體中進行別的操作。

第一時間想到:

private void Form1_Load(object sender, EventArgs e)
{
    while(true) 
    { 
        string time = DateTime.Now.ToString();
        textBox1.Text = time;
        Thread.Sleep(1000);
    }
}

這個顯然是錯誤的,甚至連窗口都跳不出來,更別提還可以在窗口中執(zhí)行其他操作。因為這個while(true)循環(huán)堵死了這個窗體的加載,根本加載不出來,會一直停留在加載這個環(huán)節(jié)。

然后就應該想到使用新開線程來解決這個問題了:

private void Form1_Load(object sender, EventArgs e)
{
    // Control.CheckForIllegalCrossThreadCalls = false; 
    // 如果加上上面這行代碼,就可以運行了,但是只是忽略了線程安全性
    Thread timeShower = new Thread(ShowTime);
    timeShower.IsBackground = true;
    timeShower.Start();

}
private void ShowTime()
{
    while(true)
    {
        string time = DateTime.Now.ToString();
        textBox1.Text = time;      
    }
}

很可惜,線程的第一次使用報錯了!跨線程訪問了textbox控件,這不安全。

接著,我們想使用委托的相關知識來解決這個問題:

private void Form1_Load(object sender, EventArgs e)
{
    // 這個線程傳遞的是我們使用委托的那個函數(shù)
    Thread timeShower = new Thread(Textbox1ShowTime);
    timeShower.IsBackground = true;
    timeShower.Start();

}
private delegate void TextBox1Delegrate(); // 聲明了一個無參無返回的委托
private void Textbox1ShowTime()
{
    // 實例化委托,傳遞實際需要調(diào)用的ShowTime函數(shù)
    TextBox1Delegrate newDelegrate = new TextBox1Delegrate(ShowTime);
    while (true) 
    {
        this.Invoke(newDelegrate); // 這里的this代指Form1
        Thread.Sleep(1000);
    }
}
private void ShowTime()
{
    string time = DateTime.Now.ToString();
    textBox1.Text = time;
}

好的,我們已經(jīng)基本上完成了我們設想的情景。但是,我們能否一步到位,將while循環(huán)和Thread.Sleep(1000) 放到ShowTime函數(shù)中,然后直接使用委托啟動ShowTime,就可以直接得到實時刷新的時間呢?

如果你和我一樣聰明,估計會這么想:

private void Form1_Load(object sender, EventArgs e)
{
    Thread timeShower = new Thread(Textbox1ShowTime);
    timeShower.IsBackground = true;
    timeShower.Start();

}
private delegate void TextBox1Delegrate();
private void Textbox1ShowTime()
{
    TextBox1Delegrate newDelegrate = new TextBox1Delegrate(ShowTime);
    this.Invoke(newDelegrate);
}
private void ShowTime()
{
    while (true)
    {
        string time = DateTime.Now.ToString();
        textBox1.Text = time;
        Thread.Sleep(1000);
    }
}

好的,恭喜你,獲得了一個無響應卡死的窗口!這是為什么呢?首先我們要知道this.Invoke(function)的原理是將這個function發(fā)送到創(chuàng)建this的這個線程,在這里也就是主線程中執(zhí)行,因此來規(guī)避跨線程問題。但是這里,我們傳了一個永真循環(huán)進去,其實和我們一開始想到的那個代碼沒有本質(zhì)上的區(qū)別,能顯現(xiàn)出窗口只是因為我們使用了委托(那還是進步了,恭喜我們)。

那么應該怎么樣才能實現(xiàn)一步到位的解決這個實時顯示時間的問題呢?

好,其實我也實現(xiàn)不了一步到位,如果有大神會的,一定要在評論區(qū)教教我QAQ

但是可以實現(xiàn)一個兩重外包,然后實現(xiàn)直接調(diào)用委托實時顯示時間。究其根本,只要不把永真的while循環(huán)放到主線程中執(zhí)行,就可以了,所以這兩重外包就是把while單獨分離了(其實上面那種實現(xiàn)方法也是同樣的思路,分離了永真while這個討厭的東西)

private void Form1_Load(object sender, EventArgs e)
{
    Thread timeShower = new Thread(Textbox1ShowTime);
    timeShower.IsBackground = true;
    timeShower.Start();

}
private delegate void TextBox1Delegrate();
private void Textbox1ShowTime()
{
    TextBox1Delegrate newDelegrate = new TextBox1Delegrate(ShowTime);
    newDelegrate.Invoke();
}
private void ShowTime()
{
    while (true)
    {
        TextBox1Delegrate newDelegrate = new TextBox1Delegrate(ShowTime1);
        this.Invoke(newDelegrate);
        Thread.Sleep(1000);
    }
}
private void ShowTime1()
{
    string time = DateTime.Now.ToString();
    textBox1.Text = time;
}

同樣的,順著分離這個思路走下去其實還可以進一步精簡我們的代碼。

這邊要提到一個屬性 textBox1.InvokeRequired。這個屬性是bool類型,他表示獲取一個值,該值指示調(diào)用方在對控件進行方法調(diào)用時是否因為調(diào)用的方法于不在創(chuàng)建控件的線程中,是否必須調(diào)用 Invoke 方法。

對于有許多控件和線程需要進行操作的情況而言,這個屬性可以非常方便的確定是否需要使用Invoke()方法進行委托的調(diào)用

對于我們這個從子線程Textbox1ShowTime()進去的ShowTime而言,這個屬性永遠是真的;而對于我們使用this.Invoke(newDelegrate)使用的ShowTime而言,它相當于是在主線程中執(zhí)行這個函數(shù),從而實現(xiàn)了狀態(tài)的分離。因此我們可以通過if(textBox1.InvokeRequired)實現(xiàn)將ShowTime1放到ShowTime中實現(xiàn)

private void Form1_Load(object sender, EventArgs e)
{
    Thread timeShower = new Thread(Textbox1ShowTime);
    timeShower.IsBackground = true;
    timeShower.Start();

}
private delegate void TextBox1Delegrate();
private void Textbox1ShowTime()
{
    TextBox1Delegrate newDelegrate = new TextBox1Delegrate(ShowTime);
    newDelegrate.Invoke();
}
private void ShowTime()
{
    if (textBox1.InvokeRequired) // 從new Thread(Textbox1ShowTime)調(diào)用的委托一定會進這里
    {
        while (true)
        {
            TextBox1Delegrate newDelegrate = new TextBox1Delegrate(ShowTime);
            this.Invoke(newDelegrate); // 這個相當于是在主線程調(diào)用委托
            Thread.Sleep(1000); // 注意這個sleep不能放到else中,不然會無限的休眠
        }
    }
    else // 從主線程調(diào)用的委托一定會進到這里
    {
        string time = DateTime.Now.ToString();
        textBox1.Text = time;
    }

}

OK,針對這個跨線程訪問的例子暫時我只能想這么多了,有錯誤的話大佬們不靈賜教啊QAQ

到此這篇關于C# winform 窗體控件跨線程訪問的實現(xiàn)的文章就介紹到這了,更多相關C# winform跨線程訪問內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關文章

最新評論