ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

WPF_21_多线程

2022-02-10 12:33:40  阅读:166  来源: 互联网

标签:21 对象 代码 调度 backgroundWorker 线程 WPF 多线程


多线程编程可使WPF应用程序执行后台工作,同时保持用户界面能够进行响应。
WPF支持单线程单元(Single-Thread Apartment)模型:

  • 元素具有线程关联性 - 创建元素的线程拥有该元素,其他线程不能直接与该元素进行交互
  • 具有关联性的WPF对象都继承自DispatcherObject类
  • UI线程运行整个应用程序并拥有所有WPF对象

Dispatcher

调度程序(dispatcher)拥有应用程序线程,并管理工作项队列。当程序运行时,调度程序接受新的工作请求,并且一次执行一个任务。

从技术上看,当在新线程中第一次实例化 DispatcherObject类的派生类时,会创建调度程序。如果创建线程相互独立,并用它们显示独立的窗口,最终将创建多个调度程序。不过,大部分都保持简单方式,坚持使用一个用户界面线程和一个调度程序。

DispatcherObject

DispatcherObject实例是绑定到调度程序线程的对象,提供了三个成员:

名称 说明
Dispatcher 返回管理该对象的调度程序
CheckAccess() 如果代码在正确的线程上使用对象,返回true
VerifyAccess() 如果代码在正确的线程上使用对象,就什么都不做,否则抛出 InvalidOperationException 异常

WPF对象为保护自身会频繁调用 VerifyAccess() 方法,从而不可能在错误的线程中长时间使用一个对象。

  • Dispatcher.BeginInvoke() 方法会将代码安排为调度程序的任务
  • Dispatcher.Invoke() 方法将指定的代码封送到调度程序线程,并且会拖延线程直到调用程序执行你的代码

BackgroundWorker

有很多方法执行异步操作,比如创建 Thread 对象。可以创建几十个线程,但如果访问共享数据就需要使用锁定机制来避免潜在错误。另外频繁创建线程或大量创建线程会产生额外的,不必要的开销。

BackgroundWorker用于简化 Windows 窗体应用程序中与线程相关的问题。为单独线程中运行耗时的任务提供了简单的方法,它在后台使用调度程序,并使用基于事件的模型对封送问题进行抽象。可以使用两个方法创建实例:

  • 在代码中创建 BackgroundWorker 对象,并使用代码关联事件处理程序
  • 可在 XAML 中声明 BackgroundWorker 对象。这种优点是可使用特性关联事件处理程序
<Window.Resources>
    <cm:BackgroundWorker x:Key="backgroundWorker"
        WorkerReportsProgress="True" WorkerSupportsCancellation="True"
        DoWork="backgroundWorker_DoWork"
        ProgressChanged="backgroundWorker_ProgressChanged"
        RunWorkerCompleted="backgroundWorker_RunWorkerCompleted"/>
</Window.Resources>
  1. 调用 RunWorkerAsynce() 方法时,可提供一个对象,然后会被传递到 DoWork 事件中。
  2. 当开始执行后,从CLR线程池提取一个自由线程,然后从这个线程引发DoWork事件.此时不能访问共享数据或用户界面
  3. 完成耗时工作就会引发 RunWorkerCompleted事件通知应用程序。这个事件在调度程序线程引发,此时可以访问共享数据和用户界面

跟踪进度

如果要实现 跟踪进度 ,首先DoWork事件处理代码需要调用 ReportProgress() 方法,并提供已经完成的百分比。每次调用 ReportProgress() 方法时,都会引发 ProgressChanged 事件,此事件是从用户界面线程引发的,所以不需要使用 Dispatcher.BeginInvoke()方法。

private void UpdateProgress()
{
    backgroundWorker.ReportProgress(p);
}

private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // 此事件在UI线程
    progressBar.Value = e.ProgressPercentage;
}

支持取消

为请求取消,代码需要调用 CancelAsync() 方法。此时不会自动发生任何操作,相反执行任务的代码(DoWork方法)需要显示地检查取消请求,执行所有清除操作,然后返回。

private void cmdCancel_Click(object sender, RoutedEventArgs e)
{
    // 手动触发取消
    backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkerEventArgs e)
{
    // 省略代码
    if (backgroundWorker.CancellationPending)
    {
        e.Cancel = true;
        return;
    }
    // 省略代码

    e.Result = result;
}

当取消操作时,仍会引发 RunWorkerCompleted 事件,此时可检查任务是否已经被取消并进行相应处理。

private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if(e.Cancelled)
    {
        MessageBox.Show("Search cancelled.");
    }
    else if(e.Error != null)
    {
        MessageBox.Show(e.Error.Message, "An Error Occurred");
    }
    else
    {
        // 正常结束耗时任务
    }

    progressBar.Value = 0;
}

我的公众号 HelloProgram

标签:21,对象,代码,调度,backgroundWorker,线程,WPF,多线程
来源: https://www.cnblogs.com/jqwang/p/15878576.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有