WPF_21_多线程

多线程编程可使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

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WPF中使用OpenTK进行多线程渲染可以通过以下步骤实现: 1. 创建一个继承自`System.Windows.Threading.DispatcherObject`的类,该类将用于在WPF主线程上执行OpenGL渲染操作。 ```csharp public class OpenGLRenderer : DispatcherObject { private GLControl glControl; public OpenGLRenderer(GLControl control) { glControl = control; } public void Render() { Dispatcher.Invoke(() => { // 在WPF主线程上执行OpenGL渲染操作 glControl.MakeCurrent(); // 执行OpenGL渲染操作 // ... glControl.SwapBuffers(); }); } } ``` 2. 在WPFUI线程上创建一个OpenGL渲染控件,并将其添加到WPF窗口中。 ```csharp GLControl glControl = new GLControl(); // 设置OpenGL控件的属性和事件处理程序 // ... // 创建OpenGL渲染器,并将OpenGL控件传递给它 OpenGLRenderer renderer = new OpenGLRenderer(glControl); // 将OpenGL控件添加到WPF窗口中 myWindow.Children.Add(glControl); ``` 3. 创建一个后台线程,用于执行OpenGL渲染操作。 ```csharp Thread renderThread = new Thread(() => { while (true) { // 执行OpenGL渲染操作 renderer.Render(); // 控制渲染帧率,可根据实际需求进行调整 Thread.Sleep(16); // 60帧/秒 } }); renderThread.Start(); ``` 在这个例子中,我们创建了一个继承自`DispatcherObject`的类`OpenGLRenderer`,它用于在WPFUI线程上执行OpenGL渲染操作。然后在WPFUI线程上创建了一个OpenGL渲染控件,并将其添加到WPF窗口中。最后,在一个后台线程上执行OpenGL渲染操作,通过调用`OpenGLRenderer`的`Render`方法来在WPF主线程上执行OpenGL渲染操作。 需要注意的是,在多线程渲染时,需要确保线程安全,避免多个线程同时访问OpenGL渲染上下文。在上述示例中,我们使用了`DispatcherObject`来确保在WPF主线程上执行OpenGL渲染操作,从而避免了多线程访问OpenGL渲染上下文的冲突。 希望这些信息对你有帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值