wpf 多线程

简介:

一、线程概述:【引用MSDN】

 通常,WPF 应用程序从两个线程开始:一个用于处理呈现,一个用于管理 UI。呈现线程有效地隐藏在后台运行,而 UI 线程则接收输入、处理事件、绘制屏幕以及运行应用程序代码。大多数应用程序都使用一个 UI 线程,但在某些情况下,最好使用多个线程。我们将在后面举例说明这一点。

UI 线程对一个名为 Dispatcher 的对象内的工作项进行排队。Dispatcher 基于优先级选择工作项,并运行每一个工作项,直到完成。每个 UI 线程都必须至少有一个 Dispatcher,并且每个 Dispatcher 都只能在一个线程中执行工作项。

要构建响应速度快、且用户友好的应用程序,诀窍是减小工作项,以最大限度地提高 Dispatcher 吞吐量。这样,工作项将永远不会因为在 Dispatcher 队列中等待处理而失效。输入与响应之间的任何可察觉的延迟都会使用户不快。

那么,WPF 应用程序应如何处理大型操作呢?如果您的代码涉及大型计算,或者需要查询某台远程服务器上的数据库,应怎么办呢?通常的办法是在单独的线程中处理大型操作,而专门让 UI 线程来负责处理 Dispatcher 队列中的工作项。当大型操作完成时,可以将结果报告给 UI 线程来显示。

一直以来,Windows 只允许创建 UI 元素的线程访问这些元素。这意味着负责某项长时间运行任务的后台线程无法更新已完成的文本框。Windows 这样做是为了确保 UI 组件的完整性。如果列表框的内容在绘制过程中被后台线程更新,那么该列表框看上去将会很奇怪。

WPF 使用一种内置互斥机制来强制执行这种协调。WPF 中的大多数类都派生自 DispatcherObjectDispatcherObject 在构造时存储对链接到当前所运行线程的 Dispatcher 的引用。实际上,DispatcherObject 与创建它的线程关联。在程序执行过程中,DispatcherObject 可以调用它的公共 VerifyAccess 方法。VerifyAccess 检查与当前线程关联的 Dispatcher,并将它与构造过程中存储的 Dispatcher 引用进行比较。如果两者不匹配,VerifyAccess 将引发异常。VerifyAccess 用于在每个属于 DispatcherObject 的方法的开头调用。

如果只有一个线程可以修改 UI,那么后台线程如何与用户交互呢?后台线程可以请求 UI 线程代表它执行操作。这是通过向 UI 线程的 Dispatcher 注册工作项来完成的。Dispatcher 类提供两个注册工作项的方法:Invoke 和 BeginInvoke。这两个方法均调度一个委托来执行。Invoke 是同步调用,也就是说,直到 UI 线程实际执行完该委托它才返回。BeginInvoke 是异步的,将立即返回。

Dispatcher 按优先级对其队列中的元素进行排序。向 Dispatcher 队列中添加元素时可指定 10 个级别。这些优先级在 DispatcherPriority 枚举中维护。有关 DispatcherPriority 级别的详细信息可以在 Windows SDK 文档中找到。

 二、下面是线程的一个小例子

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row= "0"  Orientation= "Horizontal"  VerticalAlignment= "Center"  >
        <Button Content= "StartWithThread" 
            Click= "StartOrStop"
            Name= "startStopButton"
            Margin= "5,0,5,0"
            />
        <TextBlock Margin= "10,5,0,0" >Biggest Prime Found:</TextBlock>
        <TextBlock Name= "bigPrime"  Margin= "4,5,0,0" >3</TextBlock>
    </StackPanel>
 
        <StackPanel Grid.Row= "1"  Orientation= "Horizontal"  VerticalAlignment= "Center"  >
        <Button Content= "MessageBox.show" 
          
            Name= "startStopButton2"
            Margin= "5,0,5,0"  Click= "startStopButton2_Click"  />
        </StackPanel>
        <StackPanel Grid.Row= "2"  Orientation= "Horizontal"  VerticalAlignment= "Center"  >
            <Button Content= "StartWithoutThread"    
            Name= "startStopButton3"
            Margin= "5,0,5,0"  Click= "startStopButton3_Click"  />
            <TextBlock Margin= "10,5,0,0" >data:</TextBlock>
            <TextBlock Name= "myData"  Margin= "4,5,0,0" >3</TextBlock>
        </StackPanel>
    </Grid>

  

后台代码:

/// <summary>
    /// Interaction logic for ThreadTest.xaml
    /// </summary>
    public  partial  class  ThreadTest : Window
    {
        public  delegate  void  NextPrimeDelegate();
 
        //Current number to check
        private  long  num = 3;
 
        private  bool  continueCalculating = false ;
 
 
        public  ThreadTest()
        {
            InitializeComponent();
        }
 
 
        private  void  StartOrStop( object  sender, RoutedEventArgs e)
        {
            if  (continueCalculating)
            {
                continueCalculating = false ;
                startStopButton.Content = "Resume" ;
            }
            else
            {
                continueCalculating = true ;
                startStopButton.Content = "Stop" ;
                startStopButton.Dispatcher.BeginInvoke(
                    DispatcherPriority.Normal,
                    new  NextPrimeDelegate(CheckNextNumber));
            }
        }
 
        public  void  CheckNextNumber()
        {
            // Reset flag.
            NotAPrime = false ;
 
            for  ( long  i = 3; i <= Math.Sqrt(num); i++)
            {
                if  (num % i == 0)
                {
                    // Set not a prime flag to true.
                    NotAPrime = true ;
                    break ;
                }
            }
 
            // If a prime number.
            if  (!NotAPrime)
            {
                bigPrime.Text = num.ToString();
            }
 
            num += 2;
            if  (continueCalculating)
            {
                startStopButton.Dispatcher.BeginInvoke(
                    System.Windows.Threading.DispatcherPriority.SystemIdle,
                    new  NextPrimeDelegate( this .CheckNextNumber));
            }
        }
 
        private  bool  NotAPrime = false ;
 
        private  void  startStopButton2_Click( object  sender, RoutedEventArgs e)
        {
            MessageBox.Show( "Hello Thread" );
        }
 
        private  void  startStopButton3_Click( object  sender, RoutedEventArgs e)
        {
           long  n = 0;
 
            // If a prime number.
            while  (n < 10000000)
            {
 
                myData.Text = n.ToString();
                n++;
            }
 
          
        }
 
    }

  效果图1:当运行"Stop"按钮时,在单击“MessageBox.show"按钮,则能弹出窗口HelloThead。

 效果图2。当单击“StartWithoutThrad"按钮时,此时再单击“MessageBox.show"按钮,则提示Not Responding。因为此时没有用到线程。

 参考资料:http://msdn.microsoft.com/zh-cn/library/ms741870.aspx#threading_overview

三、 下面是使用线程的三种方式

 原文:http://www.cnblogs.com/iupme/archive/2011/05/18/2049949.html

第1种用 Task类. 推荐用这个办法

复制代码
public   void  工作_Task()
{
Dispatcher x 
=  Dispatcher.CurrentDispatcher; // 取得当前工作线程
// 另开线程工作
Task < int >  计数  =   new  Task < int > (()  =>  {  return  计数方法(); });
计数.ContinueWith(工作完毕后方法);
// 工作完毕后执行的方法
计数.Start(); // 开始工作

}
public   void 工作完毕后方法(Task < int >  参数)
{
if  (参数.IsCompleted)  // 正常工作完毕
{
var 结果 
=  参数.Result;  // 取得结果
// 处理结果.
// 本方法非界面线程.如果需要在界面线程操作,需要转移到界面线程
}
}

int  c;
public   int  计数方法()
{
return  c ++ ;
}
复制代码

第2种方法用线程.

复制代码
public   void  工作_Thread()
{
Dispatcher x 
=  Dispatcher.CurrentDispatcher; // 取得当前工作线程
// 另开线程工作
System.Threading.ThreadStart start  =   delegate ()
{
// 工作函数
Func < string >  fu  =   new  Func < string > (()  =>  {  return   "" ; }); // 工作函数
var 工作结果  =  fu(); // 开始工作

// 异步更新界面
x.BeginInvoke( new  Action(()  =>
{
// 在界面线程操作 可以使用 工作结果
}), DispatcherPriority.Normal); 
};
new  System.Threading.Thread(start).Start();  // 启动线程
}
复制代码

第3种方法用 BackgroundWorker.

这种方法介绍的比较多了.就不说了.

复制代码
BackgroundWorker 后台线程;
public   void 线程初始化()
{
后台线程 
=   new  BackgroundWorker();
后台线程.WorkerSupportsCancellation 
=   true // 可以取消
后台线程.DoWork  +=   new  DoWorkEventHandler(后台线程_DoWork);
后台线程.RunWorkerCompleted 
+=   new  RunWorkerCompletedEventHandler(后台线程_RunWorkerCompleted); 
}
public   void  启动后台线程()
{
后台线程.RunWorkerAsync();
}

void  后台线程_RunWorkerCompleted( object  sender, RunWorkerCompletedEventArgs e)
{
// 工作完毕的方法
}

void  后台线程_DoWork( object  sender, DoWorkEventArgs e)
{
// 工作方法
}
复制代码

 


本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2011/09/01/2043139.html,如需转载请自行联系原作者

目录
相关文章
|
监控 C# C++
VS+C#+WPF多线程视频摄像头播放器监控
VS+C#+WPF多线程视频摄像头播放器监控
322 0
VS+C#+WPF多线程视频摄像头播放器监控
|
C#
WPF QuickStart系列之线程模型(Thread Model)
原文:WPF QuickStart系列之线程模型(Thread Model) 这篇博客将介绍WPF中的线程模型。 首先我们先来看一个例子,用来计算一定范围内的素数个数。 XAML: ...
1085 0
|
数据可视化 C# 容器
WPF 多线程 UI:设计一个异步加载 UI 的容器
原文 WPF 多线程 UI:设计一个异步加载 UI 的容器 对于 WPF 程序,如果你有某一个 UI 控件非常复杂,很有可能会卡住主 UI,给用户软件很卡的感受。但如果此时能有一个加载动画,那么就不会感受到那么卡顿了。
1684 0
|
C#
WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口)
原文 WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口) WPF 的 UI 逻辑只在同一个线程中,这是学习 WPF 开发中大家几乎都会学习到的经验。如果希望做不同线程的 UI,大家也会想到使用另一个窗口来实现,让每个窗口拥有自己的 UI 线程。
3090 0
|
调度 C# Windows
WPF 线程:使用调度程序构建反应速度更快的应用程序
原文:WPF 线程:使用调度程序构建反应速度更快的应用程序 作者:Shawn Wildermuth 原文:http://msdn.
994 0
|
C#
WPF中窗口控件的跨线程调用
原文:WPF中窗口控件的跨线程调用 在WinForm中,我们要跨线程访问窗口控件,只需要设置属性CheckForIllegalCrossThreadCalls = false;即可。 在WPF中要麻烦一下,同样的不允许跨线程访问,因为没有权限,访问了会抛异常; 没有CheckForIllegalCrossThreadCalls 属性,怎么办? 在WPF中的窗口控件都有一个Dispatcher属性,允许访问控件的线程;既然不允许直接访问,就告诉控件我们要干什么就好了。
1990 0
|
C#
WPF异常捕获三种处理 UI线程, 全局异常,Task异常
原文:WPF异常捕获三种处理 UI线程, 全局异常,Task异常 protected override void OnStartup(StartupEventArgs e){base.OnStartup(e);RegisterEvents();} private void RegisterEvents(){//TaskScheduler.
2537 0
|
C#
WPF解决方案------调用线程无法访问此对象,因为另一个线程拥有该对象
WPF [调用线程无法访问此对象,因为另一个线程拥有该对象。] 解决方案 在这里以播放图片为例进行说明,代码如下: void _Timer_Elapsed(object sender, ElapsedEventArgs e) { this.
5053 0
|
C# 安全
在WPF 4.5中跨线程更新集合
原文:在WPF 4.5中跨线程更新集合 WPF中一个非常强大的功能是数据绑定,我们可以把一个集合绑定到ListBox中,当集合的数据发生变更时,ListBox界面也会同步变更。本身这是一个非常美好的事情,但是美中不足的是:当把集合绑定到ListBox中的时候,集合也顺带继承了ListBox的这种不能夸线程访问的限制。
1108 0
|
C# 算法
WPF 利用子线程弹出子窗体的研究
原文:WPF 利用子线程弹出子窗体的研究   一般来说子线程都是用来处理数据的,主窗体用来实现展现,但是有些时候我们希望子窗体实现等待效果,遮挡主窗体并使主窗体逻辑正常进行,这个业务需求虽然不多,但是正好我们用到了,于是我打算把研究成果写在这了。
1500 0