zoukankan      html  css  js  c++  java
  • 线程间通讯:WaitHandler使用实例及分析

    实例效果:

    1.点击“启动线程”会启动一个线程t每隔2秒在listbox上插入一条新记录。

    2.点击“关闭线程”会停止线程t,但不是马上停止而是等待线程t当次循环的工作后再结束。

    Form1.cs

     1 using System;
    2 using System.Collections.Generic;
    3 using System.ComponentModel;
    4 using System.Data;
    5 using System.Drawing;
    6 using System.Linq;
    7 using System.Text;
    8 using System.Windows.Forms;
    9 using System.Threading;
    10
    11 namespace TestThread
    12 {
    13 public partial class Form1 : Form
    14 {
    15 private ManualResetEvent Stop = new ManualResetEvent(false);//用于告诉线程t要关闭t线程
    16 private ManualResetEvent Stoped = new ManualResetEvent(false);//用于告诉主线程t线程已关闭
    17 Thread t = null;
    18 private delegate void SetUIDelegate(string val);//用于线程t操作ui控件
    19
    20 public Form1()
    21 {
    22 InitializeComponent();
    23 }
    24
    25 private void button1_Click(object sender, EventArgs e)//“启动线程”
    26 {
    27 MyThread mt = new MyThread(Stop, Stoped);
    28 mt.UIEvent += (val) =>
    29 {
    30 if (lbx.InvokeRequired)
    31 lbx.Invoke(new SetUIDelegate(SetUI), val);
    32 else
    33 lbx.Items.Add(val);
    34 };
    35 t = new Thread(() =>
    36 {
    37 mt.Run();
    38 });
    39 t.Start();
    40 }
    41
    42 private void button2_Click(object sender, EventArgs e)//“关闭线程”
    43 {
    44 if (null!=t&&t.IsAlive)//若线程t存在并存活才需关闭
    45 {
    46 Stop.Set();//发出指令告诉线程t:你是时候死了!
    47
    48 while (t.IsAlive)//因线程t不是马上自尽(也许还要吃个包、喝口茶在上吊哦!),所以要继续检查它是否存活
    49 {
    50 if (Stoped.WaitOne(0, false))//阻塞当前线程(这里设置阻塞0秒),就是看看线程t死了没
    51 {
    52 button1.Enabled = false;
    53 }
    54 Application.DoEvents();//因ui线程一直在检查线程t的死活,弄得其他需要ui线程的处理都无法进行,加上这句ui线程就有时间理睬一下其他处理了,以免画面假死!
    55 }
    56 }
    57 }
    58
    59 private void SetUI(string val)
    60 {
    61 lbx.Items.Add(val);
    62 }
    63 }
    64 }

    MyThread.cs

     1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Threading;
    6
    7 namespace TestThread
    8 {
    9 public delegate void SetUI(string val);
    10
    11 class MyThread
    12 {
    13 private ManualResetEvent Stop = null;
    14 private ManualResetEvent Stoped = null;
    15 public event SetUI UIEvent;
    16
    17 public MyThread(ManualResetEvent stop,ManualResetEvent stoped)
    18 {
    19 Stop = stop;
    20 Stoped = stoped;
    21 }
    22
    23 public void Run()
    24 {
    25 int i = 0;
    26 while (true)
    27 {
    28 Thread.Sleep(2000);//睡2秒再工作吧!
    29 UIEvent(i.ToString());//操作ui控件
    30 i++;
    31 if (Stop.WaitOne(0, false))//阻塞当前线程(这里又只阻塞0秒),直到ui线程赐死线程t
    32 {
    33 Stoped.Set();//告诉ui线程它要自杀了
    34 break;//自杀去了
    35 }
    36 }
    37 }
    38 }
    39 }

    子线程之死可以有两种方式:1、ui线程中调用t.Abort(),为它杀,也许线程t还有些事没做但已经没有机会了;2、让线程t退出或调用Thread.CurrentThread.Abort(),为自杀,这样线程t就可以在临死前了结心愿了。而上述功能就属于让线程t自杀,下面进一步分析。

    线程t无缘无故是不会自杀的,而ui线程要它自刎就必须发出一条命令,而这条命令就是ManualResetEvent对象。先看一看类结构

    可以看到ManualResetEvent类有一个孪生兄弟AutoResetEvent类,它们的祖父是WaitHandle类。

    ManualResetEvent实例有终止和非终止两个状态,在初始化时可以设定。它的Set()方法会将实例设为终止状态,Reset()方法会将实例设为非终止状态。而WaitOne()就是阻塞当前线程直到实例被设为终止状态,而WaitOne()方法有多个重载方法,可以设定阻塞时间,超过了阻塞时间实例状态依然为非终止的话就放弃阻塞,让线程继续执行WaitOne语句以下的内容。WaitOne返回值为Boolean值,表示实例状态是否为终止状态。

    AutoResetEvent类跟ManualResetEvent类只有一点区别就是它会自动把实例设为非终止状态。

    而使用WaitHandle的静态方法WaitAll或WaitAny可以检查多个ManualResetEvent实例和AutoResetEvent实例的状态。

    上述代码中线程t自杀时通过另一个ManualResetEvent实例告诉ui线程“我挂了!”,好让ui线程做善后工作。

    检查线程t是否已死的过程是一直占用ui线程的,而窗口上控件的交互也是由ui线程来处理,这时会出现画面假死的状态,如果发出了调用ui线程处理其他事件的话就会有异常。这时加上一句Application.DoEvents()表示让处理当前消息队列中的所有window消息,就是说ui线程抽出一部分时间来处理消息队列中的其他消息(如界面的交互),而不是完成了第一个消息再着手后面的消息。注意:这时ui线程是可用的,只是正忙于处理第一个消息,如果ui线程挂起来了、阻塞了或死了Application.DoEvents()无法使让ui线程处理消息队列中的其他消息。


    要实现上述的子线程自杀方式也可以用两个静态变量来做控制,至于实现方法我这里就不写了。



    欢迎添加我的公众号一起深入探讨技术手艺人的那些事!

    如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!
      

  • 相关阅读:
    Advanced Configuration Tricks
    Reviewing the Blog Module
    Editing and Deleting Data
    Making Use of Forms and Fieldsets
    Understanding the Router
    SQL Abstraction and Object Hydration
    Preparing for Different Databases
    Java学习理解路线图
    Openstack学习历程_1_视频
    CentOS安装Nginx负载
  • 原文地址:https://www.cnblogs.com/fsjohnhuang/p/2319253.html
Copyright © 2011-2022 走看看