zoukankan      html  css  js  c++  java
  • 基于WPF+XMPP的IM程序开发日志 之二 WPF线程模型

      循例地在开始正文前说些废话。正如这篇博客的题目——开发日志,这系列的博客是我在编写这个IM的一些日志,或者另外一个说法:笔记。并不是一些系统的文章,例如“XX学习教程”。这些博客里面的内容主要记录我碰到的问题及对问题领悟,免得日后碰到这些问题又去google一番,而并不是一些“如何编写IM程序”的教程。

     一:WPF线程模型。 

       除非已经对WPF体系结构非常熟悉,对多线程开发很了解,不然我们在与WPF打交道的时候经常会遇到这样一个异常: 

    由于其他线程拥有此对象,因此调用线程无法对其进行访问。(The calling thread cannot access this object because a different thread owns it)

    在WPF中,天生拥有两个线程,一个线程用于渲染UI,另一个线程是管理UI(这个我们称之为UI线程)。传说中android的动画效果为什么没有iphone的动画效果好,就是因为iphone的绘制渲染的线程的优先级非常高,只要有关于动画的操作,比如说滑动一个菜单,那么这个动画会被安排到最优先级运行,从而保证动画的流畅。这我没有深入研究,所以可能技术上来说以上描述不是十分正确,但可以按这个方式去理解。大概WPF中也是这样的理念。UI线程创建了那些在XAML或者在c#中定义的控件,并且拥有他们,并且出于对UI的保护,其他线程是不能访问到UI线程里的东西的,如果我们新建一个线程,然后在这个线程里面修改一个在xaml中或者在主线程中定义的Button.Content,那么就会得到这个异常。

       在IM开发过程中,使用agsXMPP库的时候,agsXMPP有很多事件,比如XmppClientConnection.OnStateChanged事件,OnError事件等等,我们会用到很多事件处理函数,在这里必须注意一点就是,当这些事件被触发,代码被执行到事件处理函数里面的时候,执行代码的线程往往不是主线程(这里“往往”不知道用的对不对,反正我碰到的都不是在主线程中执行的),也就是说,如果这时在事件处理函数中写这样的代码:button1.content="something",就会抛出由于其他线程拥有此对象,因此调用线程无法对其进行访问异常。我们可以再visual studio中调试代码时候在看到当前执行代码的是主线程还是其他线程:如果线程一栏中没有写明是“主线程”,那么当前执行代码的线程就不是主线程。

       这个时候,假如我们必须在其他线程中访问控件,怎么办?这就需要通过Dispatcher了。WPF中大多数控件都继承自DispatcherObject,也就拥有Dispatcher属性,这个Dispatatcher具体是什么东西,我就不写了,因为我也不知道,但显浅的来说,它是对他所属的线程进行工作调度的这么一个对象,或者说线程的一个管家,或者中介。你要在一个拥有某个控件的线程的外部(或者说其他线程)访问这个线程的控件,就只能通过这个控件的Dispatcher来处理了,Dispatcher有两个方法:Invoke和BeginInvoke,用来对外开放访问这个Dispatcher所属的线程所拥有的控件的机会。比如我想在其他线程中访问主线程的Button:

     

             private void OnEventFired(object sender, MouseButtonEventArgs e)

            {
                btn.Dispatcher.Invoke(new Action(delegate { button1.Content = "some text"; }));
            }

    假设OnEventFired正被其他线程执行,代码里面通过btn.Dispatcher的Invoke方法执行一个(匿名的)函数,该函数里面是设置buttton1.content。Invoke和BeginInvoke前者是即时调用和异步调用

        二:Thread.ApartmentState

    有时我们会遇到这样的异常:调用线程必须为 STA,因为许多 UI 组件都需要。 (The calling thread must be STA, because many UI components require this)

     这是因为在WPF中,不是什么线程都可以创建控件的,或者说:在WPF中,不是所有控件都能在非STA线程中创建的。这个STA全称 Single Thread Apartment,另一个对应的是MTA (Multi Thread Apartment)。STA 线程是COM里面的概念(具体请另行google),因为WPF底层应该与COM有关联,新创建一个控件时某些地方与COM有勾搭,所以一个线程想新建一个控件,这个线程往往需要设置成STA(Thread.ApartmentState属性)。为什么主线程可以新建一个控件?因为主线程是STA线程。

    以上主要说了一下WPF的一些概念和技术点,但由于我还没有非常深入去研究,所以以上内容中很多地方可能说得不对,即使目前我是这样理解。另一方面,个人感觉要解决技术问题,还是上外国一些网站,例如stackoverflow.com中找一些资料,这就是为什么我将异常的英文也一同写上,便于在google in enlish中搜索。在google上一搜国内的资料,会发现10篇文章中,有8篇是转载第九篇的,而第9篇却是只说了其然,而不说其所以然的。而搜一些英文网站,往往可以看到老外的回复不只是"mark",还有很多深入到原因,或者详细原理里面去解释,令人更好地了解某个技术点。

  • 相关阅读:
    MySQL的Limit 性能差?真的不能再用了?
    天天写order by,你知道Mysql底层如何执行吗?
    微信小程序 rich-text使用正则去除html中img标签中的css样式
    微信小程序开发加入版本更新提示并自动更新
    keepass 用户名显示星号的问题
    firebase/php-jwt使用openssl实现 RSA非对称加密
    Homstead ubuntu 系统pip3的安装
    sqlserver 重置自增列种子值 违反了 PRIMARY KEY 约束的处理
    ghost 安装系统出现EFI PART红色错误的问题
    在laravel 5.6中接管dingo/api 错误
  • 原文地址:https://www.cnblogs.com/lwhkdash/p/2505617.html
Copyright © 2011-2022 走看看