zoukankan      html  css  js  c++  java
  • [WPF] Felix 的线程学习笔记(二)——从WPF入手,实现简单的多线程

     

    第二篇 从WPF入手,实现简单的多线程 

    此贴接昨天发的笔记,这次我了解了WPF的多线程大致实现原理,并简单编写了一个多线程程序。 

    先将几个常用的概念列一下:

    线程关联度WPF属于创建它的线程,并且不能被其他的线程直接访问。当一个对象被关联到一个单线程时,就认为它是一个单线程对象,并且认为该对象具有线程关联度。

    同步:同步可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令。 这样的好处是能避免读写时的数据错误。

    异步:执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程。在此期间可以进行别的过程,当系统接收到之前那个调用的返回值或消息时,系统会自动触发委托,开始处理返回值。

    下面以两段WPF的代码为例,说明如何利用委托来实现异步线程,我使用的是Visual Studio 2008

    第一段代码

    前台代码
    <Window x:Class="ThreadTest.Window1"
        xmlns
    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml"
        Title
    ="Window1" Height="300" Width="300">
        
    <Grid>
            
    <Border Width="200" Height="225" BorderBrush="Black" BorderThickness="1" Margin="4">
                
    <StackPanel>
                    
    <TextBox Height="19" Name="textBox1" VerticalAlignment="Top" />
                    
    <Button HorizontalAlignment="Left" Name="button1" Width="75" Click="button1_Click">Button1</Button>
                    
    <Button HorizontalAlignment="Left" Name="button2" Width="75" Click="button2_Click">Button2</Button>
                    
    <Label Height="28" Name="UIThreadId" Width="120" Content=""/>
                
    </StackPanel>
            
    </Border>
        
    </Grid>
    </Window> 

     后台代码

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Threading;

    namespace ThreadTest
    {
        
    /// <summary>
        
    /// Interaction logic for Window1.xaml
        
    /// </summary>
        public partial class Window1 : Window
        {
            
    public Window1()
            {
                InitializeComponent();
                
    this.UIThreadId.Content = this.Dispatcher.Thread.ManagedThreadId;
            }

            
    private void button1_Click(object sender, RoutedEventArgs e)
            {
                
    this.textBox1.Text = "Sleeping";
                Thread.Sleep(
    5000);
            }

            
    private void button2_Click(object sender, RoutedEventArgs e)
            {
                
    this.textBox1.Text = "Hello WPF";
            }
        }
    }

         这段代码中,button1调用的过程中由于Sleep(5000)的存在,一直占用着线程,此时我们可以发现,按button2不会有反应,因为WPFUI默认的是单线程模式(STA)。只有当原来的函数退出线程了,第二个函数才会进入线程,更改textbox里的内容。

     

        要想让UI能自由的变化,首先我们要为另一个函数另开一个线程,之后我们使用委托和分发器(Dispatcher)来实现另一个线程中的函数对主线程中UI的更改。可以说,使用分发器的过程,就是我们实现异步线程的过程。

     

        那么委托和分发器又是如何实现异步线程的呢?

    我的理解是:分发器的作用在于给不同线程的函数安排执行的顺序。委托相当于函数的包装,让这些函数过程可以被包装进入分发器等候调遣。比喻可能不大恰当,欢迎指正^_^

    这样,在不同线程上的函数可以被分发器统一调度、执行,执行完后有了返回值,再触发处理返回值的委托,不断进行下去。

    下面以代码为例,说明异步线程的产生。

    第二段代码:

     前台代码

    <Window x:Class="MutipleThreads.Window1"
        xmlns
    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml"
        Title
    ="Window1" Height="300" Width="300">
        
    <Grid>
            
    <Border Width="200" Height="225" BorderBrush="Black" BorderThickness="1" Margin="4">
                
    <StackPanel>
                    
    <TextBox Height="19" Name="textBox1" VerticalAlignment="Top" />
                    
    <Button HorizontalAlignment="Left" Name="button1" Width="75" Click="button1_Click">Button1</Button>
                    
    <Button HorizontalAlignment="Left" Name="button2" Width="75" Click="button2_Click">Button2</Button>
                
    </StackPanel>
            
    </Border>
        
    </Grid>
    </Window>
    后台代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Windows.Threading;
    using System.Threading;

    namespace MutipleThreads
    {
        
    /// <summary>
        
    /// Interaction logic for Window1.xaml
        
    /// </summary>
        public partial class Window1 : Window
        {
            
    private delegate void ThreadDelegate(); //申明一个专用来调用更改线程函数的委托

            
    public Window1()
            {
                InitializeComponent();
            }

            
    private void button1_Click(object sender, RoutedEventArgs e)
            {
                ThreadDelegate backWorkDel 
    = new ThreadDelegate(prcessStart); //创建一个ThreadDelegate的实例,调用准备在后台运行的函数
                backWorkDel.BeginInvoke(nullnull);//使用异步的形式开始执行这个委托
            }

            
    private void button2_Click(object sender, RoutedEventArgs e)
            {
                
    this.textBox1.Text = "Awake";   //主线程中更改界面,不要别的代码
            }

            
    private void prcessStart()          //这个就是我们将要在后台执行的函数
            {
                ThreadDelegate changeTetBoxDel 
    = delegate()  //后台中要更改主线程中的UI,于是我们还是用委托来实现,再创建一个实例
                {
                    
    this.textBox1.Text = "Sleeping";
                };
    //要调用的过程
                this.Dispatcher.BeginInvoke(DispatcherPriority.Send, changeTetBoxDel); //使用分发器让这个委托等待执行

                Thread.Sleep(
    3000);
            }
            
        }
    }

        还是两个按钮,都要更改textbox1的内容,不同的是当按下Button1时,我们不是直接更改其内容,而是另外开了一个线程,并指明以异步方式运行。这样更改UI的函数就在另一个线程里运行了。但是问题又来了,WPF规定,UI只能被主线程所访问,那么在另一个线程中的prcessStart()如何更改UI呢?我们用Dispatcher和委托来实现,先实例化委托,在为其委托创建好要调用的过程,再使用分发器让它等待执行。DispatcherPriority.Send是我们为其规定的优先级。

          运行后可以发现界面可以自由的更改。 这就是多线程的作用。

          一般来说,我们都会把需要不断运算而很少改动界面的过程作为后台运行,从而解放出前台的线程来和用户交互。

          第二篇笔记到此结束~ 

     ---------------------------------------------------------------------------------------------------------------------------------------

          66℃绿茶空间原创,转载请注明出处,感谢博客园!

  • 相关阅读:
    2. Add Two Numbers
    1. Two Sum
    22. Generate Parentheses (backTracking)
    21. Merge Two Sorted Lists
    20. Valid Parentheses (Stack)
    19. Remove Nth Node From End of List
    18. 4Sum (通用算法 nSum)
    17. Letter Combinations of a Phone Number (backtracking)
    LeetCode SQL: Combine Two Tables
    LeetCode SQL:Employees Earning More Than Their Managers
  • 原文地址:https://www.cnblogs.com/felixfang/p/1709281.html
Copyright © 2011-2022 走看看