开发者社区> zting科技> 正文

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,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口)
原文 WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口) WPF 的 UI 逻辑只在同一个线程中,这是学习 WPF 开发中大家几乎都会学习到的经验。如果希望做不同线程的 UI,大家也会想到使用另一个窗口来实现,让每个窗口拥有自己的 UI 线程。
2380 0
WPF 多线程 UI:设计一个异步加载 UI 的容器
原文 WPF 多线程 UI:设计一个异步加载 UI 的容器 对于 WPF 程序,如果你有某一个 UI 控件非常复杂,很有可能会卡住主 UI,给用户软件很卡的感受。但如果此时能有一个加载动画,那么就不会感受到那么卡顿了。
1431 0
拒绝卡顿——在WPF中使用多线程更新UI
原文:拒绝卡顿——在WPF中使用多线程更新UI 有经验的程序员们都知道:不能在UI线程上进行耗时操作,那样会造成界面卡顿,如下就是一个简单的示例:     public partial class MainWindow : Window    {        public MainWindow(...
2617 0
WPF多线程访问控件
大家知道WPF中多线程访问UI控件时会提示UI线程的数据不能直接被其他线程访问或者修改,该怎样来做呢? 分下面两种情况 1.WinForm程序 1)第一种方法,使用委托: private delegate void SetTextCallback(string text); ...
734 0
WPF 标签化文本框控件
WPF 标签化文本框控件
28 0
如何解决WPF中 ScrollViewer 内包含 TreeView 或者 ListBox 等控件时滚轮事件被劫持的问题
如何解决WPF中 ScrollViewer 内包含 TreeView 或者 ListBox 等控件时滚轮事件被劫持的问题
67 0
WPF 点击 Datagrid 中的TextBox 控件获取其所在行的数据
WPF 点击 Datagrid 中的TextBox 控件获取其所在行的数据
37 0
WPF 获取列表中控件的同时,选中其所在行
WPF 获取列表中控件的同时,选中其所在行
20 0
在WPF中使用winform控件WebBrowser
在WPF中使用winform控件WebBrowser
43 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载