[C#参考]UI和线程(一)

[C#参考]UI和线程(一)

Windows是一个多任务的系统,如果你使用的是windows 2000及其以上版本,你可以通过任务管理器查看当前系统运行的程序和进程。

什么是进程呢?当一个程序开始运行时,它就是一个进程,进程所指包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又是由多个线程所组成的,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

一 关于Thread的说明

在.net framework class library中,所有与多线程机制应用相关的类都是放在System.Threading命名空间中的。其中提供Thread类用于创建线程,ThreadPool类用于管理线程池等等,此外还提供解决了线程执行安排,死锁,线程间通讯等实际问题的机制。如果你想在你的应用程序中使用多线程,就必须包含这个类。Thread类有几个至关重要的方法,描述如下:

  • Start():启动线程 
  • Sleep(int):静态方法,暂停当前线程指定的毫秒数 
  • Abort():通常使用该方法来终止一个线程 
  • Suspend():该方法并不终止未完成的线程,它仅仅挂起线程,以后还可恢复。 
  • Resume():恢复被Suspend()方法挂起的线程的执行

线程入口使程序知道该让这个线程干什么事在C#中,线程入口是通过ThreadStart代理(delegate)来提供的你可以把ThreadStart理解为一个函数指针,指向线程要执行的函数当调用 Thread.Start()方法后,线程就开始执行ThreadStart所代表或者说指向的函数。 ThreadState在各种情况下的可能取值如下:

  • Aborted:线程已停止 
  • AbortRequested:线程的Thread.Abort()方法已被调用,但是线程还未停止 
  • Background:线程在后台执行,与属性Thread.IsBackground有关 
  • Running:线程正在正常运行 
  • Stopped:线程已经被停止 
  • StopRequested:线程正在被要求停止 
  • Suspended:线程已经被挂起(此状态下,可以通过调用Resume()方法重新运行) 
  • SuspendRequested:线程正在要求被挂起,但是未来得及响应 
  • Unstarted:未调用Thread.Start()开始线程的运行 
  • WaitSleepJoin:线程因为调用了Wait(),Sleep()或Join()等方法处于封锁状态

二 Winform中使用的thread

首先可以看看最直接的方法,也是.net 1.0下支持的方法。但请注意的是,此方法在.net 2.0以后就已经是一种错误的方法了。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        Thread thread = new Thread(ThreadFuntion);
        thread.IsBackground = true;
        thread.Start();
    }
    private void ThreadFuntion()
    {
        while (true)
        {
            this.textBox1.Text = DateTime.Now.ToString();
            Thread.Sleep(1000);
        }
    }
}

这段code在vs2005或者2008上都抛出异常 :Cross-thread operation not valid:Control ‘textBox1’ accessed from a thread other than the thread it was created on . 这是因为.net 2.0以后加强了安全机制,不允许在winform中直接跨线程访问控件的属性。那么怎么解决这个问题呢,下面提供几种方案。

第一种方案: 在Thread创建之前,将Control.CheckForIllegalCrossThreadCalls 设为 false。 此代码告诉编译器:在这个类中我们不检查跨线程的调用是否合法(如果没有加这句话运行也没有异常,那么说明系统以及默认的采用了不检查的方式)。然而,这种方法不可取。我们查看CheckForIllegalCrossThreadCalls 这个属性的定义,就会发现它是一个static的,也就是说无论我们在项目的什么地方修改了这个值,他就会在全局起作用。而且像这种跨线程访问是否存在异常,我们通常都会去检查。如果项目中其他人修改了这个属性,那么我们的方案就失败了,我们要采取另外的方案。

第二种方案:

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        //子线程计数器
        private int counter; 

        public Form1()
        {
            InitializeComponent();
        }//Form1

        private void button1_Click(object sender, EventArgs e)
        {
             Thread thread = new Thread(new ThreadStart(StartWorkFromUIThread));
             //设置线程为后台线程,这样的话前台UI线程死亡时它也跟着死亡
              //如果设置为前台线程,UI线程死亡后这个线程是不会死亡的,所以即使关闭了窗口,程序也不会结束。因为有前台线程在运行
              thread.IsBackground = true;
            thread.Start();

        }//button1_Click

        //子线程执行序
        private void StartWorkFromUIThread()
        {
            while(true)
            {
                if(this.InvokeRequired)
                {
                  //Invoke的作用就是让后面的函数到主线程中去执行
                    //所以在Invoke里面的函数中,不能有Sleep,这样会卡死界面
                    BeginInvoke(new EventHandler(RunsOnWorkerThread), null);
                }
                else
                {
                    RunsOnWorkerThread(this,null);
                }
                Thread.Sleep(500);
            }
            
        }//StartWorkFromUIThread

        //要在主线程中做相应的工作
        private void RunsOnWorkerThread(object sender, EventArgs e)
        {
            //计数器计算调用的次数
              counter++;
            lblTimeShow.Text = DateTime.Now.ToString()+ " " + counter.ToString();
        }//RunsOnWorkerThread

    }//class
}

上面代码知识点的解释:

InvokeRequired,获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。上面的this就是拥有控件的窗体,这句话就是测试一下当前的位置是不是在UI线程中。不是的话返回true,是的话返回false。

如果控件的 Handle 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。

如果不需要 Invoke(调用发生在同一线程上),或者如果控件是在另一个线程上创建的但尚未创建控件的句柄,则 InvokeRequired 可以返回 false。


总结:

在Winform多线程中,由于子线程是不能直接操作UI线程中的控件的,所以利用Invoke(函数,参数),让子线程能调用一个函数完成对控件的操作,当然这个函数也就会在UI线程中运行,这个函数中不能有过多的计算和sleep的出现,否则会阻塞UI线程,界面卡死。

Invoke是很好理解的,UI线程(主线程)可以new一个新的子线程,然后告诉它去执行哪个函数;同样的,子线程可以起让UI主线程去执行一个函数。

通过上叙代码,可以看到问题已经被解决了,通过等待异步,我们就不会总是持有主线程的控制,这样就可以在不发生跨线程调用异常的情况下完成多线程对winform多线程控件的控制了。

转载于:https://www.cnblogs.com/stemon/p/4208327.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/109600.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Alex 的 Hadoop 菜鸟教程: 第17课 更快速的MapReduce – Spark「建议收藏」

    Alex 的 Hadoop 菜鸟教程: 第17课 更快速的MapReduce – Spark「建议收藏」Spark是Apache的顶级项目。项目背景是Hadoop的MapReduce太挫太慢了,于是有人就做了Spark,目前Spark声称在内存中比Hadoop快100倍,在磁盘上比Hadoop快10倍。

  • oracle导出错误904,exp 导出 EXP-00056: 遇到 ORACLE 错误 904

    oracle导出错误904,exp 导出 EXP-00056: 遇到 ORACLE 错误 904同一台机器上,其中一个备份正常<1>,另外一个报错<2>。同一台机器上导出日志显示的版本居然不一样,哪个高人给指点下。我机器上装的是Oracle9iEnterpriseEditionRelease9.2.0.8.0-Production(client)<1>连接到:Oracle9iEnterpriseEditionRelease9.2…

  • 踩坑啥意思_年轻人踩坑

    踩坑啥意思_年轻人踩坑Unity小科普老规矩,先介绍一下Unity的科普小知识:Unity是实时3D互动内容创作和运营平台。包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助Unity将创意变成现实。Unity平台提供一整套完善的软件解决方案,可用于创作、运营和变现任何实时互动的2D和3D内容,支持平台包括手机、平板电脑、PC、游戏主机、增强现实和虚拟现实设备。也可以简单把Unity理解为一个游戏引擎,可以用来专业制作游戏!

  • mac如何杀进程_Mac结束进程

    mac如何杀进程_Mac结束进程比如我们想要杀死端口9999对应的进程首先,查看端口9999的占用情况lsof-itcp:9999其次,执行kill-9pid,这里我的pid=9760kill-99760

  • 传统请求风格 VS RestFul 风格

    传统请求风格 VS RestFul 风格RestFul风格概念Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。功能资源:互联网所有的事物都可以被抽象为资源资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。分别对应添加、删除、修改、查询。传统方式操作资源:通过不同的参数来实现不同的效果!方法单一,post和get​ http://127.0.0.1/item/queryItem.actio

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号