计时器小程序——由浅入深实例讲解

  本菜在实现简单的计时器过程中遇到问题的一些成长笔记,有不完整观点的话请多多见谅,也看了众多大神的博客才整理的笔记,下面来实现个人写的小程序。

首先第一个实例(很简单):

  winform窗体包含两个控件:label、button控件,点击控件开始计时,代码如下:

namespace Timer_Test
{
    public partial class CommonInstance : Form
    {
        private static int startTime = 0;

        public CommonInstance()
        {
            InitializeComponent();
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            GetTimes(); //方法在外部封装,调用即可,你应该知道封装的好处咯
        }
        //写个方法,用于计时运算,不能是静态方法,static和this不能共存;
        private void GetTimes()
        {
            startTime += 1;
            this.lb_getTimes.Text = Convert.ToString(startTime);
        }
    }
}

  哈哈,这个很简单实现吧,那么怎样才能让计时器自动计时呢,相信你已经想到timer控件,没错接下来实现下!

  实现代码如下:

namespace Timer_Test
{
    public partial class CommonInstance : Form
    {
        private static int startTime = 0;

        public CommonInstance()
        {
            InitializeComponent();
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            //GetTimes();
            timer1.Start();
        }
private void timer1_Tick(object sender, EventArgs e)
        {
            startTime += 1;
            lb_getTimes.Text = startTime.ToString();
        }
        private void CommonInstance_Load(object sender, EventArgs e)
        {
            //窗体加载时设置好timer的Enable属性为true:可用
            timer1.Enabled = true;
            timer1.Interval = 1000; //设置间隔时间为1s
        }
    }
}

  其实你会发现,使用timer控件实现也很简单嘛,很多工作都是timer自己做了,省事多了,但我写这编文字的重点不仅仅这些,下面来说下重点吧:

  多线程实现的计时器,不用timer控件了,自己写个机制实现它(这会让我们学到更多的知识),老实说,我还是第一次接触线程,刚开始真的是摸

  不着头脑咋用来着捏,下面就细细说来.....

使用线程前,先引入命名空间:using System.Threading;    具体代码如下:

namespace Timer_Test
{
    public partial class CommonInstance : Form
    {
        private static int startTime = 0;

        Thread thread = null;//声明一个线程

        public CommonInstance()
        {
            InitializeComponent();
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            //GetTimes();
            //timer1.Start();

            thread = new Thread(new ThreadStart(GetTimes)); //创建开始线程实例,将要执行的方法作为参数
            thread.Start();
        }
         private void GetTimes()
        {
            startTime += 1;
            this.lb_getTimes.Text = Convert.ToString(startTime);
        }
}

  

  这时你就会暗暗自喜,线程也不过如此嘛,当你点击开始计时后就报错了哦,报错是你给lable控件赋值之时,为什么会错呢,因为你跨线程给UI界面控件赋值了,这关系

到数据的安全问题。

 

  c#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它。此时它将会在内部调用new MethodInvoker(LoadGlobalImage)来完成下面的步骤,这个做法保证了控件的安全,你可以这样理解,有人想找你借钱,他可以直接在你的钱包中拿,这样太不安全,因此必须让别人先要告诉你,你再从自己的钱包把钱拿出来借给别人,这样就安全了。

这样就可以很好的理解上面那个winform程序中 this.BeginInvoke(fc);这行code了,这个执行后其实也就是主线程在调用fc中绑定的方法ThreadFuntion()了,这种方式其实相当于把这个新开的线程“注入”到了主控制线程中,它取得了主线程的控制。只要这个线程不返回,那么主线程将永远都无法响应。就算新开的线程中不使用无限循环,使可以返回了。这种方式的使用多线程也失去了它本来的意义。(copy过来的,源地址在最后面)

  

  解决方法一:在点击事件里加这句代码:Control.CheckForIllegalCrossThreadCalls = false;      // 默认为true;

  意思是:不检查跨线程,这是不安全的,不推荐使用;

  解决方法二:(这篇文章的意义所在了)

  通常的方法是使用委托delegate委托主线程处理(解释是后面的啰嗦),和BeginInvoke()方法、IsBackground属性值(默认为false:即非后台线程)以及属

性InvokeRequired;

  下面就要用到InvokeRequired这个propety

  

解决问题代码如下:

namespace Timer_Test
{
    public partial class CommonInstance : Form
    {
        private delegate void FlushClient(); //定义一个委托代理,前面要和委托代理函数签名一致

        private static int startTime = 0;

        Thread thread = null;//声明一个线程

        public CommonInstance()
        {
            InitializeComponent();
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            //GetTimes();
            //timer1.Start();

            thread = new Thread(new ThreadStart(GetTimes));
            thread.IsBackground = true;  //设置为后台线程
            thread.Start();
        }

        //写个方法,用于计时运算,不能是静态方法,static和this不能共存;
        private void GetTimes()
        {
            while (true)  //无限循环
            {
                Thread.Sleep(1000);  //睡眠时间为一秒
                ThreadFunction();
            }

        }
        //写个委托代理函数
        private void ThreadFunction()
        {
                if (this.InvokeRequired) //等待异步
                {
                    FlushClient fc = new FlushClient(ThreadFunction);//实例化委托
                    this.BeginInvoke(fc); //通过代理刷新UI
                }
                else
                {
                    startTime += 1;
                    this.lb_getTimes.Text = Convert.ToString(startTime);
                }
        }

  这里我就啰嗦一下咯........

  怎么说呢:这时你应该弄清楚 线程 是如何工作的?

  在C#中,非主线程(即非UI线程,就是通过new Thread创建的线程)是不能直接操作UI元素的,必须通过Handler与UI线程通讯,通知UI线程更新.而C#则采用委托的方式更

新UI元素。

  线程分为前台线程和后台线程,线程默认为前台线程(主线程),这意味着任何前台线程在运行都会保持程序存活。

后台线程:只要有一个前台线程在运行,应用程序的进程就在运行。如果多个前台线程在运行,而Main()方法结束了,应用程序的进程就是激活的,直到所有前台线程完成其任务为止。

前台线程和后台线程的唯一的区别是— 后台线程不会阻止进程终止。

在默认情况下,用Thread 类创建的线程都是前台线程。线程池中的线程总是后台线程。

参考:http://www.cnblogs.com/Linford-Xu/archive/2012/09/19/2693340.html

时间: 2024-10-13 20:09:14

计时器小程序——由浅入深实例讲解的相关文章

02——微信小程序官方demo讲解——app部分

第一节讲了目录结构,这节主要讲解下目录中app.js部分. 它由三部分组成app.js.app.json与app.wxss 1.JS部分 1.1概述 //app.js App({ onLaunch: function () { // 展示本地存储能力 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) // 登录 wx.login({ success

微信小程序入门实例之记事本

主要实现思想都在代码的注释中,项目源码见github 首先上项目目录 app.js文件代码如下: //app.js App({ onLaunch: function() { //调用API从本地缓存中获取数据 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) }, getUserInfo: function(cb) { var that = thi

Vant+小程序+购物车实例

图片实例,看是否是您所需要的喔.... 扫码小程序可看实例操作,有啥问题也可扫码加群,很希望可以帮助到你喔!           HTML部分: <view class="cart"> <view class='top' wx:if="{{isTop}}"> <view> <van-icon name="delete" class='delete' bindtap="emptyCart"

分享一下微信小程序的实例【转】

wx-gesture-lock  微信小程序的手势密码 WXCustomSwitch 微信小程序自定义 Switch 组件模板 WeixinAppBdNovel 微信小程序demo:百度小说搜索 shitoujiandaobu 小程序:石头剪刀布(附代码说明) audiodemo 微信小程序开发之视频播放器 Video 弹幕 弹幕颜色自定义 star 微信小程序开发之五星评分 switchCity 微信小程序开发之城市选择器 城市切换 huadong_del  微信小程序滑动删除效果 jianh

01——微信小程序官方demo讲解——文件结构

1.环境概览 首先环境配置的部分略过,打开小程序开发工具.选择一个空目录,即可开始一个demo项目. 其中新建成功后的目录如图所示: 2.文件结构描述 如图所示,左边是界面展示,右边是目录结构. 目录大体分为3部分:app部分.pages部分.其他部分. app主要放置一些全局性的变量以及配置.就相当于浏览器中的windows.node中的global. pages中放的是一个一个的页面.一个页面由一个文件夹组成,文件夹内由四部分组成,js,wxml,wxss,json. 其中js主要是该页面的

03——微信小程序官方demo讲解——page部分

一个page由一个文件夹以及文件夹下四个文件组成. 比如一个页面叫index.则需要在pages目录下新建一个index目录,且包含由index+类型(js\wxml\wxss\json)为名组成的若干文件.文件名格式为硬性要求. 1.JS部分 1.1概述 Page({ data: { logs: [] }, onLoad: function () { this.setData({ logs: (wx.getStorageSync('logs') || []).map(log => { retu

Chronometer组件实现计时器小程序

main.xml代码如下: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"

微信小程序用户评分实例

微信小程序用户评分实例!成都小程序开发实例为大家分享了微信小程序实现展示评分结果的具体代码,供大家参考,具体内容如下 根据评分展示整颗行星或者半颗星星 根据评分按照小数点展示整颗行星或者部分星星 wxml 本文实例为大家分享了微信小程序实现展示评分结果的具体代码,供大家参考,具体内容如下 根据评分展示整颗行星或者半颗星星 根据评分按照小数点展示整颗行星或者部分星星 wxml <view class="conmmentbox"> <view class="st

微信小程序学习指南

作者:初雪链接:https://www.zhihu.com/question/50907897/answer/128494332来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 微信小程序正式公测, 张小龙全面阐述小程序,定档1月9日上线(附90分钟演讲全文) ... 前言:新人第一坑,跳坑指南:修改后,必须保存:ctrl+S: 1:官方工具:https://mp.weixin.qq.com/debug/w ... tml?t=1476434678461 2:简易教