C# 线程基础

1. 线程的基本概念

简单的讲进程就是程序分配在内存当中,等待处理器进行处理,请记住线程会消耗大量的操作系统资源。多个线程共享一个物理处理器将导致处理器忙于处理管理这些进程,而无法运行程序。

使用线程通常是一个操作系统的任务,试图在一个单核CPU上并行执行计算任务是没有任何意义的,可能比顺序执行花费的时间更长。

为了更好的利用现代处理器的能力,使用多线程处理程序发挥出最好的作用,这需要组织多个线程间的通讯和相互同步。

下面将学习一下 线程的生命周期,和创建线程、挂起线程、线程等待、以及终止线程。

创建一个线程操作

        static void Main(string[] args)
        {
            //创建一个新的线程来实现输出数字
            Thread t = new Thread(PrintNumber);
            t.Start();
            //这一行代码是在创建了一个新的线程并行执行的
            PrintNumber();
            Console.ReadLine();
        }

        static void PrintNumber()
        {
            Console.WriteLine("Starting...");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(i);
            }
        }

从上面代码可以看出一个程序始终有一个主线程在执行,而Thread是创建一个新的线程执行。两者之间是同步执行的。

暂停线程

        static void Main(string[] args)
        {
            //创建一个带暂停的进程
            Thread t = new Thread(PrintNumberWithdelay);
            t.Start();
            //这一行就是始终执行的主线程经过(一个程序都有一个始终执行的主线程)
            PrintNumber();
        }

        static void PrintNumber()
        {
            Console.WriteLine("Starting...");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(i);
            }
        }
        static void PrintNumberWithdelay()
        {
            Console.WriteLine("暂停...");
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine(i);
            }
        }

很明显,主线程已经执行完毕,而新的线程在输出暂停以后还在继续执行。每次执行都会休眠2秒钟。

线程等待

那么如何不让主线程继续往下执行,而是等待新线程执行完毕再往下执行呢。

        static void Main(string[] args)
        {
            //创建一个带暂停的进程
            Thread t = new Thread(PrintNumberWithdelay);
            t.Start();
            t.Join();//线程等待
            Console.WriteLine("执行完毕了");

        }
        static void PrintNumberWithdelay()
        {
            Console.WriteLine("暂停...");
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.WriteLine(i);
            }
        }

终止线程

        static void Main(string[] args)
        {
            //创建一个带暂停的进程
            Thread t = new Thread(PrintNumberWithdelay);
            t.Start();
            Thread.Sleep(TimeSpan.FromSeconds(3));
            t.Abort();//终止线程
            Console.WriteLine("线程终止了");
        }

这段代码是给线程注入了ThreadAbortException方法,导致线程被终结,这样操作是非常危险的,因为该异常可能会导致整个应用程序都崩溃。

可以使用 ResetAbort 方法来拒绝被终止。

检测线程状态

        static void Main(string[] args)
        {
            //创建一个带暂停的进程
            Thread t = new Thread(PrintNumberWithdelay);
            Thread t1 = new Thread(DoNothing);
            t1.Start();
            t.Start();
            for (int i = 0; i < 30; i++)
            {
                Console.WriteLine(t.ThreadState.ToString());
            }
            Thread.Sleep(TimeSpan.FromSeconds(3));
            t.Abort();//终止线程
            Console.WriteLine("线程终止了");
            Console.WriteLine(t.ThreadState.ToString());
            Console.WriteLine(t1.ThreadState.ToString());
        }

        static void PrintNumberWithdelay()
        {
            Console.WriteLine("开始啦...");
            Console.WriteLine(Thread.CurrentThread.ThreadState.ToString());
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.WriteLine(i);
            }
        }
        static void DoNothing()
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
        }

当主程序定义了两个不同的线程,一个将会被终止,而另一个则会成功运行。线程状态位于Thread 对象的ThreadState属性中。ThreadState属性是一个C#枚举对象。

刚开始线程状态为Unstarted ,然后启动线程,并估计在一周期为30的迭代的区间中,线程状会从Running变为WitSleepJoin。

线程优先级

    class ThreadSample
    {
        private bool _isStopped = false;
        public void Stop()
        {
            _isStopped = true;
        }
        public void CountNumbers()
        {
            long counter = 0;
            while (!_isStopped)
            {
                counter++;
            }
            Console.WriteLine("{0} 和 {1,11}" + " count={2,13}",
                Thread.CurrentThread.Name,
                Thread.CurrentThread.Priority,
                counter.ToString("NO"));
        }
    }

    class Program
    {
        static void RunThreads()
        {
            var sample = new ThreadSample();
            var threadOne = new Thread(sample.CountNumbers);
            threadOne.Name = "ThreadOne";
            var threadTwo = new Thread(sample.CountNumbers);
            threadTwo.Name = "ThreadTwo";

            threadOne.Priority = ThreadPriority.Highest;//优先级较高
            threadTwo.Priority = ThreadPriority.Lowest;//优先级较低

            threadOne.Start();
            threadTwo.Start();

            Thread.Sleep(TimeSpan.FromSeconds(2));
            sample.Stop();

        }
        static void Main(string[] args)
        {
            Console.WriteLine("线程状态:{0}",Thread.CurrentThread.Priority);
            Console.WriteLine("开始");
            RunThreads();
            Thread.Sleep(TimeSpan.FromSeconds(3));
            Console.WriteLine("模拟CPU单核计算");
            //让操作系统运行在第一个CPU第一个核心上
            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
            RunThreads();//运行时间很长
            Console.WriteLine("线程终止了");
        }

    }

此程序只用于演示,通常中无需使用这种方式。

前台线程和后台线程

    class ThreadSample
    {
        private readonly int _iterations;
        public ThreadSample(int iterations)
        {
            _iterations = iterations;
        }
        public void CountNumbers()
        {
            for (int i = 0; i < _iterations; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} 和 {1}",
                    Thread.CurrentThread.Name, i);
            }

        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var samppleforegroud = new ThreadSample(10);
            var sampplebackgroup = new ThreadSample(20);

            var threadOne = new Thread(samppleforegroud.CountNumbers);

            threadOne.Name = "前台";

            var threadTwo = new Thread(sampplebackgroup.CountNumbers);

            threadTwo.Name = "后台";
            threadTwo.IsBackground = true;

            threadOne.Start();
            threadTwo.Start();

        }

    }

显示创建的是前台线程, ThreadTwo是后台线程 ,通过配置第一个线程会比第二个线程先完成,前台线程如果执行完毕,那么也会把后台线程终止掉。

向线程传递参数

class ThreadSample
    {

        private readonly int _iterations;
        public ThreadSample(int iterations)
        {
            _iterations = iterations;
        }
        public void CountNumbers()
        {
            for (int i = 0; i < _iterations; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} 和 {1}",
                    Thread.CurrentThread.Name, i);
            }

        }
    }

    class Program
    {
        static void Count(object i)
        {
            CountNumbers((int)i);
        }

        static void CountNumbers(int number)
        {
            Console.WriteLine(number);
        }

        static void PrintNumber(int number)
        {
            Console.WriteLine(number);
        }

        static void Main(string[] args)
        {
            var samppleforegroud = new ThreadSample(10);

            var threadOne = new Thread(samppleforegroud.CountNumbers);
            threadOne.Name = "One";
            threadOne.Start();
            threadOne.Join();
            Console.WriteLine("------------");

            var threadTwo = new Thread(Count);
            threadTwo.Name = "Two";
            threadTwo.Start(8);
            threadTwo.Join();

            var threaThree = new Thread(() => CountNumbers(12));
            threaThree.Name = "Three";
            threaThree.Start();
            threaThree.Join();

            int i = 10;
            var threaFour = new Thread(() => PrintNumber(i));
            i = 20;
            var threaFour1 = new Thread(() => PrintNumber(i));
            threaFour.Start();
            threaFour1.Start();

        }

    }

使用Lock

abstract class CountBase
    {
        public abstract void Increment();
        public abstract void Decrement();

    }

     class Counter : CountBase
    {
        public int Count { get; private set; }

        public override void Increment()
        {
            Count++;
        }

        public override void Decrement()
        {
            Count--;
        }
    }

     class CounterWithLock : CountBase
    {
        private readonly object _syncroot = new object();

        public int Count { get; private set; }
        public override void Increment()
        {
            lock (_syncroot)
            {
                Count++;
            }
        }

        public override void Decrement()
        {
            lock (_syncroot)
            {
                Count--;
            }
        }
    }

    class Program
    {
        static void TestCouner(CountBase c)
        {
            for (int i = 0; i < 100000; i++)
            {
                c.Increment();
                c.Decrement();
            }
        }

        static void Main(string[] args)
        {
            var c = new Counter();

            var t1 = new Thread(() => TestCouner(c));
            var t2 = new Thread(() => TestCouner(c));
            var t3 = new Thread(() => TestCouner(c));

            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();

            Console.WriteLine(c.Count);

            var c1 = new CounterWithLock();

             t1 = new Thread(() => TestCouner(c));
             t2 = new Thread(() => TestCouner(c));
             t3 = new Thread(() => TestCouner(c));

             t1.Start();
             t2.Start();
             t3.Start();
             t1.Join();
             t2.Join();
             t3.Join();

             Console.WriteLine(c.Count);

        }

    }

使用Monitor 锁定资源

为了避免死锁,则使用Monitor 类 来避免死锁。

Monitor.TryEnter(lock1,TimeSpan.FromSeconds(5));
时间: 2024-07-31 14:31:09

C# 线程基础的相关文章

线程基础知识

什么是线程: 在一个程序里的一个执行路线就叫做线程(thread).更准确的定义是:线程是"一个进程内部的控制序列" 一切进程至少都有一个执行线程 进程与线程 进程是资源竞争的基本单位 线程是程序执行的最小单位 线程共享进程数据,但也拥有自己的一部分数据 线程ID 一组寄存器 栈 errno 信号状态 优先级 fork和创建新线程的区别 当一个进程执行一个fork调用的时候,会创建出进程的一个新拷贝,新进程将拥有它自己的变量和它自己的PID.这个新进程的运行时间是独立的,它在执行时几乎

线程基础:多任务处理(13)——Fork/Join框架(解决排序问题)

============== 接上文< 线程基础:多任务处理(12)--Fork/Join框架(基本使用)> 3. 使用Fork/Join解决实际问题 之前文章讲解Fork/Join框架的基本使用时,所举的的例子是使用Fork/Join框架完成1-1000的整数累加.这个示例如果只是演示Fork/Join框架的使用,那还行,但这种例子和实际工作中所面对的问题还有一定差距.本篇文章我们使用Fork/Join框架解决一个实际问题,就是高效排序的问题. 3-1. 使用归并算法解决排序问题 排序问题是

线程基础:线程池(6)——基本使用(中)

(接上文:<线程基础:线程池(5)--基本使用(上)>) 3-4.JAVA主要线程池的继承结构 我们先来总结一下上文中讨论过的内容,首先就是JAVA中ThreadPoolExecutor类的继承结构.如下图所示: ThreadPoolExecutor:这个线程池就是我们这两篇文章中介绍的重点线程池实现.程序员可以通过这个线程池中的submit()方法或者execute()方法,执行所有实现了Runnable接口或者Callable接口的任务:ThreadPoolExecutor对于这些任务的执

线程基础--线程控制

3.  线程控制 1). 线程属性 目标:可以设置 线程的 detached/join 状态,线程栈的大小和最低地址等属性. detached/join 状态的区别: 当线程处于 分离状态(detached)时,线程结束时,os立即回收资源.主线程不可以调用pthread_join获取线程退出时的返回值. 当线程处于 未分离状态(join)时,线程结束时,主线程 调用pthread_join获取线程退出时的返回值, 随后释放该线程资源. a)数据类型 pthread_attr_t b)初始化及释

操作系统核心原理-4.线程原理(上)线程基础与线程同步

我们都知道,进程是运转中的程序,是为了在CPU上实现多道编程而发明的一个概念.但是进程在一个时间只能干一件事情,如果想要同时干两件或者多件事情,例如同时看两场电影,我们自然会想到传说中的分身术,就像孙悟空那样可以变出多个真身.虽然我们在现实中无法分身,但进程却可以办到,办法就是线程.线程就是我们为了让一个进程能够同时干多件事情而发明的“分身术”. 一.线程基础 1.1 线程概念 线程是进程的“分身”,是进程里的一个执行上下文或执行序列.of course,一个进程可以同时拥有多个执行序列.这就像

线程基础(一)

2015年自己定了个目标就是开始写写博客,所以心血来潮,我也来写写一些基础的东西. 线程,网上大把多资料,自己写也不会有什么新意.不过在自己做过的一些项目中用到很多线程相关的知识,但没有真正的去总结,去归纳的,导致到了用的时候有baidu.google(google被我朝禁了...我觉得是一种悲哀).好了,下面开始我的线程基础了 1.线程的定义 线程只由相关堆栈(系统栈或用户栈)寄存器和线程控制表TCB组成.它是系统调度的最基本单位.个人的理解是线程相当于开辟一个单元去执行特定的方法过程,就涉及

C#之线程基础概述

1.概述: 每个正在操作系统上运行的应用程序都是一个进程,一个进程可以包括一个或多个进程.线程是操作系统分配处理器时间的基本单元. 在进程中可以有多个线程同时执行代码,每个线程都维护异常处理程序.调度优先级和一组系统用于在调度该线程前保存线程上下文的结构. 线程上下文包括为使线程在线程的宿主进程地址空间中无缝地执行所需的所有信息,包括线程的CPU寄存器组和堆栈. 线程是进程中的执行流程,一个进程中可以同时包括多个线程,每个线程也可以得到一小段程序的执行时间,这样一个进程就可以具有多个并发执行的线

Android多线程研究(1)——线程基础及源代码剖析

从今天起我们来看一下Android中的多线程的知识,Android入门easy,可是要完毕一个完好的产品却不easy,让我们从线程開始一步步深入Android内部. 一.线程基础回想 package com.maso.test; public class TraditionalThread { public static void main(String[] args) { /* * 线程的第一种创建方式 */ Thread thread1 = new Thread(){ @Override p

Qt 线程基础(QThread、QtConcurrent等) 2

使用线程 基本上有种使用线程的场合: 通过利用处理器的多个核使处理速度更快. 为保持GUI线程或其他高实时性线程的响应,将耗时的操作或阻塞的调用移到其他线程. 何时使用其他技术替代线程 开发人员使用线程时需要非常小心.启动线程是很容易的,但确保所有共享数据保持一致很难.遇到问题往往很难解决,这是由于在一段时间内它可能只出现一次或只在特定的硬件配置下出现.在创建线程来解决某些问题之前,应该考虑一些替代的技术 : 替代技术 注解 QEventLoop::processEvents() 在一个耗时的计

线程基础知识系列(三)线程的同步

本文是系列的第三篇,前面2篇,主要是针对单个线程如何管理,启动等,没有过多涉及多个线程是如何协同工作的. 线程基础知识系列(二)线程的管理 :线程的状态,控制,休眠,Interrupt,yield等 线程基础知识系列(一)线程的创建和启动  :线程的创建和启动,join(),daemon线程,Callable任务. 本文的主要内容 何谓线程安全? 何谓共享可变变量? 认识synchronized关键字 认识Lock synchronized vs Lock 1.何谓线程安全 多线程是把双刃剑,带