C#之:线程(Thread)

一:Threading 线程描述

在 C# 语言中线程(Thread)是包含在进程中的,它位于 System.Threading 命名空间中。
与线程有关的类同样也都在 System.Threading 命名空间中,主要的类如下表所示。

类名说明
Thread在初始的应用程序中创建其他的线程
ThreadState指定 Thread 的执行状态,包括开始、运行、挂起等
ThreadPriority线程在调度时的优先级枚举值,包括 Highest、AboveNormal、Normal、BelowNormal、Lowest
ThreadPool提供一个线程池,用于执行任务、发送工作项、处理异步I/O等操作
Monitor提供同步访问对象的机制
Mutex用于线程间同步的操作
ThreadAbortException调用Thread类中的Abort方法时出现的异常
ThreadStateExceptionThead处于对方法调用无效的ThreadState时出现的异常

Thread 类主要用于实现线程的创建以及执行,其常用的属性和方法如下表所示。

属性或方法说明
Name属性,获取或设置线程的名称
Priority属性,获取或设置线程的优先级
ThreadState属性,获取线程当前的状态
IsAlive属性,获取当前线程是否处于启动状态
IsBackground属性,获取或设置值,表示该线程是否为后台线程
CurrentThread属性,获取当前正在运行的线程
Start()方法,启动线程
Sleep(int millisecondsTimout)方法,将当前线程暂停指定的毫秒数
Suspend()方法,挂起当前线程(已经被弃用)
Join()方法,阻塞调用线程,直到某个线程终止为止
Interrupt()方法,中断当前线程
Resume()方法,继续已经挂起的线程(已经被弃用)
Abort()方法,终止线程

二:线程的创建和使用

static void Main(string[] args)
        {
            //创建线程,并指定线程开始执行时要调用的方法。也可以指定一个委托或者Lambda表达式
            Thread thread = new Thread(Function);
            //指定线程为后台线程,如果不指定为前台线程。
            thread.IsBackground = true;

            //指定线程优先级别
            /*
             * 线程在C#中有5个优先级:(从低到高)Lowest -> BelowNormal -> Normal ->AboveNormal -> Highest
             * 默认状态下是:Normal。在单核CPU中效果明显,多核CPU中效果不太明显,如果希望线程尽快执行,可以把
             * 优先级设定高一点。
             */
            thread.Priority = ThreadPriority.AboveNormal;
            //启动线程。(实际操作中,并不是立即启动,而是告诉CPU,这个线程准备好了,具体什么时候启动有CPU统一调度)
            thread.Start();


            Console.ReadKey();
        }

        private static void Function()
        {
            while(true)
            {
                Console.WriteLine(DateTime.Now.ToString());
                Thread.Sleep(1000);
            }
            
        }

运行结果:
在这里插入图片描述

三:线程的启动执行

警惕:线程不会立即执行
现在的大多数系统不是一个实时的操作系统,Window也是如此。所以不要奢望线程能够立即启动。Window内部会实现特殊的算法进行线程之间的调度。在某个时刻它会决定当前运行那个线程。这反映到最底层就是某个线程分配到一定的CPU时间,可以用来执行一小段工作(由于被分配的CPU时间很短,所以操作系统中运行者上千个线程,我们也会觉得这些应用程序也是在同时执行的)。Window会选择适当的时间根据自己的算法决定下一段的CPU时间如何调度。

**实例:**将 0 - 9 分别传入10个不同的线程,结果却事与愿违。

 static int id = 0;
        
        static void Main(string[] args)
        {
           
            for (int i = 0; i < 10; i++,id++)
            {
                Thread thread = new Thread(() =>
                {
                    Console.WriteLine(string.Format("{0}:{1}", Thread.CurrentThread.Name, id));
                });
                thread.Name = string.Format("Thread{0}", i);
                thread.IsBackground = true;
                thread.Start();
            }
            Console.ReadLine();
        }

在这里插入图片描述
怎样改变这种状况,使用同步传参方法:

   static int id = 0;
        
        static void Main(string[] args)
        {
           
            for (int i = 0; i < 10; i++,id++)
            {
                //同步传参
                NewMethod(i, id);

            }



            Console.ReadLine();
        }

        private static void NewMethod(int i,int realTimeId)
        {
            Thread thread = new Thread(() =>
            {

                Console.WriteLine(string.Format("{0}:{1}", Thread.CurrentThread.Name, realTimeId));
            });
            thread.Name = string.Format("Thread{0}", i);
            thread.IsBackground = true;
            thread.Start();
        }

在这里插入图片描述

四:线程的停止

线程终止涉及两个问题:

  1. 正如线程不能立即启动一样,线程不是说停就停。无论采用什么样的方式通知线程停止,线程都会执行完毕后,在合适的情况下停止。 thread.Abort()方法,如果线程正在执行的是一段非托管代码,那么,CLR 就不会抛出 ThreadAbortException,只有当代码继续回到 CLR 时,才会引发ThreadAbortException。
  2. 要正确停止线程,不在于调用者采用了什么行为,而是更多依赖工作线程是否主动响应调用者的停止请求,大体的机制是,如果线程需要被停止,那么线程本身就应该负责给调用者开放这样的接口,Canclen。线程在工作的同时,还要以某种频率检查Canclen标识。若检测到Canclen,线程才会停止,对于 ThreadPool(线程池)同样。

CancellationTokenSource: 线程中协作式取消关键类型。
它有一个关键属性 Token ,是一个名为 CancellationToken 的值类型。CancellationToken继而提进一步提供了布尔值类型的属性 IsCancellationRequested 作为需要取消工作的标识。CancellationToken 还有一个方法值得注意,Register方法,它负责传递一个 Action 委托,在线程停止的时候被调用。
实例Dome

public partial class MainFrom : Form
    {
        public MainFrom()
        {
            InitializeComponent();
        }
        private Thread thread;

        /// <summary>
        /// 终止线程标识。构造函数中可以给一个时间段或毫秒数时间跨度。
        /// </summary>
        CancellationTokenSource tokenSource = new CancellationTokenSource();

        private void ButStart_Click(object sender, EventArgs e)
        {
            thread = new Thread(() => {
                while(true)
                {
                    if (tokenSource.IsCancellationRequested)//检测信号
                    {
                        break;
                    }
                    string str = DateTime.Now.ToString();
                    //操作UI控件,因为不是同一个线程创建的对象,要用委托方法。
                    this.Invoke(new Action(() => {
                        ListMsg.Items.Add(str);
                    }));

                    Thread.Sleep(1000);
                }
            });
            thread.IsBackground = true;
            thread.Start();
        }

        /// <summary>
        /// 终止线程,销毁线程不能再重新启动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButStop_Click(object sender, EventArgs e)
        {
            /*
             * 线程终止涉及两个问题:
             *  一: 正如线程不能立即启动一样,线程不是说停就停。无论采用什么样的方式通知线程停止,线程都会执行
             *       完毕后,在合适的情况下停止。 thread.Abort()方法,如果线程正在执行的是一段非托管代码,
             *       那么,CLR 就不会抛出 ThreadAbortException,只有当代码继续回到 CLR 时,才会引发
             *       ThreadAbortException。
             *  二: 要正确停止线程,不在于调用者采用了什么行为,而是更多依赖工作线程是否主动响应调用者的停止请
             *       求,大体的机制是,如果线程需要被停止,那么线程本身就应该负责给调用者开放这样的接口,
             *       Canclen。线程在工作的同时,还要以某种频率检查Canclen标识。若检测到Canclen,线程才会推出
             *       对于 ThreadPool同样。
             */
            thread.Abort();
        }

        

        /// <summary>
        /// 安全终止线程
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButSafelyStop_Click(object sender, EventArgs e)
        {
            // 注册一个将在取消此 System.Threading.CancellationToken 时调用的委托。
            tokenSource.Token.Register(() => {
                txMsg.Text = "线程被终止!";
            });
            //传达取消命令
            tokenSource.Cancel();
        }

        #region 弃用的方法
        [Obsolete]
        private void ButSleep_Click(object sender, EventArgs e)
        {
            //如果线程被销毁,则引发异常
            thread.Suspend();
            txMsg.Text = "线程暂停";
        }

        [Obsolete]
        private void ButGoOn_Click(object sender, EventArgs e)
        {
            thread.Resume();
            txMsg.Text = "线程继续";
        }
        #endregion

        /// <summary>
        /// 重启线程
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButRestart_Click(object sender, EventArgs e)
        {
            try
            {
                thread.Start();
            }
            catch(Exception ex)
            {
                txMsg.Text ="线程启动异常:"+ex.Message;
            }
            
        }
    }

点击开始:会不停的输出当前时间。
实例图片

线程被停止后就不能被重启,会抛出异常。
使用被弃用的方法:Suspend() (暂停); Resume() (继续);则可以。(注意,如果线程被销毁后调用这两个方法则有异常)
在这里插入图片描述

五:唤醒休眠的线程

Interrupt 用于提前唤醒一个在Sleep的线程,Sleep方法会抛出ThreadInterruptedException异常
必须抓住这个异常,然后做唤醒后的线程处理。
Sleep 是静态方法,只能是自己主动要求休眠,别人不能命令他休眠。

例:

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private Thread t1;

        private void button1_Click(object sender, EventArgs e)
        {
            t1 = new Thread(() =>
            {

                while (true)
                {
                    try
                    {
                        MessageBox.Show("hello word");
                        Thread.Sleep(3000);
                    }
                    catch (ThreadInterruptedException) //必须抓住异常,否则程序结束
                    {
                        MessageBox.Show("谁叫我!!!");
                    }
                }
            });
            t1.IsBackground = true;
            t1.Start(); //开始
        }

        private void button2_Click(object sender, EventArgs e)
        {
            t1.Interrupt(); //唤醒t1线程
        }
    }

在这里插入图片描述

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值