多线程Thread 和 线程池ThreadPool

1.多线程

1.1进程:一个程序的运行实例,就是一个进程, 而一个进程又是由多个线程所组成的。

1.2线程:线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数

1.3句柄: 是Windows系统中对象或实例的标识,这些对象包括模块、应用程序实例、窗口、控制、位图、GDI对象、资源、文件等

1.4多线程:多线程是指程序中包含多个执行流,一般是并行状态。

Thread是.Net最早的多线程处理方式,它出现在.Net1.0时代,虽然现在已逐渐被微软所抛弃,微软强烈推荐使用Task,但从多线程完整性的角度上来说,我们有必要了解下早期多线程的是怎么处理的,以便体会.Net体系中多线程处理方式的进化.

1.4.1Thread类构造函数重载,
public delegate void ThreadStart()
一个参数ThreadStart,这是一个无参无返回值委托类型,通过其Start()方法启动线程.

public delegate void ParameterizedThreadStart(object obj);
一个参数ParameterizedThreadStart,这是含一个参数无返回值的委托类型.通过委托对象的Start(object obj)方法启动线程.

如果想传入多个参数怎么办,定义一个结构体,里面定义多个代传入的变量,然后将结构体变量传递给委托参数即可.
我们也可以自定义一个类型,传递给参数object;也可以自定义一个类,将传入委托的方法写在这个类中,这个方法可以是静态方法,也可以是静态方法,具体看项目开发需求.

如下代码示例展示了如何用Thread类创建并运行多线程并行任务:

static void Main(string[] args)
{
    //定义工作线程, 线程状态:Unstarted
    Thread workthread = new Thread(WorkMethod);

    //线程分前台线程和后台线程,前台线程即主线程,后台线程即工作线程,用户线程或服务线程,低优先级别。
    //通过将工作线程设置成后台线程,main()主程序运行结束后,则工作线程也自动退出,否则主程序会一直等待工作线程结束后才会退出.
    workthread.IsBackground = true;
    //启动工作线程, 线程状态Running
    workthread.Start("ParamFromMainThread");
    //线程的其它状态改变方法
    //workthread.Abort(); //线程状态:Stopped,用于永久地停止也即销毁托管线程。
    //workthread.Suspend(); //线程状态:Suspended
    //workthread.Resume(); //线程状态:Running
    //workthread.Interrupt(); //中断线程,状态:Interrupted
            
    while (!workthread.IsAlive);//loop unitil the work thread is activated

    //工作线程的join方法会阻塞所有其他进程,包括主线程,直到当前工作线程执行结束才停止出阻塞
    //workthread.Join();

    //main()主线程
    for (int i = 0; i <= 5; i++)
    {
        Thread.Sleep(200);//模拟主线程的操作
        Console.WriteLine("执行主线程" + (i / 5.0).ToString("p"));
    }
    Console.WriteLine("主线程结束");
}

private volatile bool _isstop;
static void WorkMethod(object obj)
{
    try
    {
        string param = (string)obj;
        for (int i = 0; i <= 20; i++)
        {
            Thread.Sleep(200);//模拟工作线程的操作
            Console.WriteLine("执行工作线程" + (i / 5.0).ToString("p"));
        }
    }
    catch(Exception ex)
    {
        Console.WriteLine("ThreadError");
    }
    finally
    { Console.WriteLine("工作线程结束"); }
}

2.后台线程:

Thread类创建的线程默认是前台线程(线程池(ThreadPool)创建的线程是后台线程).
Thread创建的线程通过Isbackground属性设置为后台线程,如果主线程终止了,则后台线程自动终止.

线程分前台线程和后台线程,前台线程即主线程,后台线程理解为工作线程,用户线程或服务线程,低优先级别,通过将工作线程设置成后台线程,调用线程终止后,则工作线程也自动退出,否则主程序会一直等待工作线程结束后才会退出,如下demo示例:

static void Main(string[] args)
{
    Thread work_normal = new Thread(WorkMethod_Norml);
    Thread work_loop = new Thread(WorkMethod_loop);
    work_normal.IsBackground = false;//该线程和Main()主线程结束,则后台线程被终止
    work_loop.IsBackground = true;
    work_loop.Start();
    work_normal.Start();
    Console.WriteLine("main thread finished");
}

static void WorkMethod_Norml()
{
    for (int i = 0; i < 10; i++)
    {
        Thread.Sleep(100);
        Console.WriteLine("Normal" + i);
    }
    Console.WriteLine("前台线程结束");
}
static void WorkMethod_loop()
{
    int i = 0;
    while (i<1000)
    {
        Console.WriteLine("Back" + ++i );
        Thread.Sleep(100);
    }
    Console.WriteLine("后台线程结束");
}

3.线程中断Inrerrupt():

当线程处于非运行状态,即当线程调用了Join或Sleep方法,线程处于阻塞状态,此时调用Interrupt(),工作线程会抛出一个中断异常,处于阻塞状态的线程会捕捉到这个异常,以提早结束阻赛状态,如下demo:

static void Main(string[] args)
{
    //Using System.Threading
            
    //定义工作线程
    Thread workThread_interrupt = new Thread(DoWork_Interrupt);
            
    //启动工作线程
    workThread_interrupt.Start();
    Console.WriteLine("请在10秒内中按任意键中断线程");
    Console.ReadKey();

    
    //当工作线程调用了Join或Sleep方法,处于非运行状态,即线程处于阻塞状态
    //此时调用Interrupt(),工作线程抛中断异常,处于阻塞状态的线程会捕捉到这个异常,结束阻赛状态.
    workThread_interrupt.Interrupt();
    
    //阻塞当前线程,即main()主线程,把主线程加入到工作线程的尾部,当工作线程执行完毕后再执行main()主线程
    workThread_interrupt.Join();


    Console.WriteLine("main thread finshed");
    Console.ReadKey();
}

static void DoWork_Interrupt()
{
    try
    {
        Console.WriteLine("工作线程预计休眠10秒");
        Thread.Sleep(10000);
        Console.WriteLine("工作作线程结束");
    }
    catch(ThreadInterruptedException ex_interrupt)
    {
        Console.WriteLine("睡眠中断");
    }
}

4.线程优先级:这个比较好理解,优先级越高,获得的资源倾斜越多:

CPU同时执行不同的线程,会尽可能多的分配时间给高优先级线程,可以按照毫秒级别进行分配,高优先级的线程操作自然就执行快一些.
ThreadPriority属性:Highest,AboveNormal,Nomral,BelowNormal,Lowest,5各级别,默认是Normal级别.(Bold 24-04-24)

static void Main(string[] args)
{
    Thread work_high = new Thread(WorkMethod_High);//定义工作线程high
    Thread work_low = new Thread(WorkMethod_low);//定义工作线程low
    work_high.Priority = ThreadPriority.Highest;//设置线程优先级
    work_low.Priority = ThreadPriority.Lowest;//设置线程优先级
    work_low.Start();//启动线程
    work_high.Start();//启动线程
    Thread.Sleep(10000);//主线程休眠,工作线程正常执行
    work_low.Abort();//模拟异常中断线程,工作线程捕获该异常
    work_high.Abort();//模拟异常中断线程,工作线程捕获该异常
    work_high.Join();//
    work_low.Join();//
    Console.WriteLine("main thread finished");
    Console.ReadKey();
}

static void WorkMethod_High()
{
    long cnt = 0;
    try
    {
        while(true)
        {
            cnt++;
        }
    }
    catch(ThreadAbortException ex)
    {
        Console.WriteLine("High:" + cnt.ToString());
    }
}
static void WorkMethod_low()
{
    long cnt_low = 0;
    try
    {
        while (true)
        {
            cnt_low++;
        }
    }
    catch (ThreadAbortException ex)
    {
        Console.WriteLine("Low:" + cnt_low.ToString());
    }
}
}

5.线程状态控制:(Bold 24-04-24)

创建线程后,UnStarted状态
Start后,调度资源开始执行后,Running状态
Thread.Sleep(XX)方法后,WaitSleepJoin状态,线程睡眠状态,不执行.
线程对象Abort()方法后,会抛出一个ThreadAbortException异常,线程停止.(VS2022已停止支持该方法,大概是操作太暴力了吧~~)
线程对象执行Join()方法后,调用线程会等待线程对象的操作完成后,才会继续往下执行,即Join()方法将线程操作加入到调用线程中去,变成同步操作了.
比如调用放线程M,创建了服务线程S后,M和S代码并行执行,如果在M线程中执行S.Joing()方法后,S线程还没有结束,则S线程的未结束操作加入到M线程中来,即M线程等待S线程操作完再继续往下执行M线程的代码.
简单说就是线程对象的Join方法,把M和S线程的异步关系,编程了同步关系,这个要好好理解.

 

6.线程池,

Net提供的线程池ThreadPool类
线程池(threadPool)创建的线程是后台线程,且不能修改线程池创建的线程为前台线程,也不能设置其优先级.
如果我们想完整的演示线程池创建的线程任务,注意此时主线程不要结束,否则创建的工作线程也就因为主线程结束了而被自动终止了.

Thread创建线程需要时间,比如几十毫秒,如果创建的线程比较多,那么会比较浪费时间,这就用到了线程池.
在线程池中预先创建N多线程是闲置状态,需要线程异步执行时,就拿出一个闲置线程来执行,把创建线程变成了分配线程,减少资源开销。

打个比方,比如我有一个项目开工,需要ABCDE...H很多工种才能完成,项目进行到什么程度需要什么样的工种,我再去找,这就类似于线程的创建。
而如果我先把需要的工种人员都招齐了,项目开工后,需要什么工种,就上什么工种,这就类似于线程池分配.(Bold 24-04-24)

.Net预设的线程池可以定义线程的最小最大线程数量,可用线程数量等.
一旦线程池中某个任务完成了,该线程就回到线程池等待线程队列中,有任务需要线程时,就从线程池分配闲置的线程执行任务,若没有闲置线程可用,就等待线程释放出来再执行.
线程池,ThreadPool类是静态类,其所有成员都是静态的.通过静态方法QueueUserWorkItem,可以将工作线程任务加到线程池队列中,有空闲线程可用时任务开始执行.
常用的委托异步调用BeginInvoke(),定时器timer,任务Task都是CLR(Common Language Runtime)的线程池.
public static bool QueueUserWorkItem(WaitCallback callBack, object state);
public static bool QueueUserWorkItem(WaitCallback callBack);
QueueUserWorkItem参数为回调函数WaitCallBack,

该委托类型定义如下:public delegate void WaitCallback(object state);

我们注意看,QueueUserWorkItem方法有两个重载,一个是只有回调函数WaitCallback,这个委托类型引用的方法,需要传入一个object参数,但是在没有传入object参数,只有方法的情况下,也能执行,原因是.Net帮我们传入了一个null, 也就是说不是没有传入参数,而是默认传入了null作为object参数值.

static void CallbackWithoutParameter(object o)
{
    Console.WriteLine("123123");
    Thread.Sleep(1000);

    Console.WriteLine("123123");
    Thread.Sleep(1000);

    Console.WriteLine("123123");
    Thread.Sleep(1000);

    Console.WriteLine("123123");
    Thread.Sleep(1000);
}
static void Main(string[] args)
{
    //.Net帮我们默认传入null,作为object参数值
    ThreadPool.QueueUserWorkItem(CallbackWithoutParameter);
    
    //手动传入null作为参数
    ThreadPool.QueueUserWorkItem(_ => CallbackWithoutParameter(null));

    //.Net帮我们默认传入null,作为object参数值
    ThreadPool.QueueUserWorkItem(_ => Console.WriteLine("线程池分配的线程正在执行...."));

    Thread.Sleep(9000);
}

下面的demo是给方法QueueUserWorkItem传入一个object参数,在QueueUserWorkItem方法内部,这个对象值给回调函数使用:

 public static bool QueueUserWorkItem(WaitCallback callBack, object state);

static void Main(string[] args)
{
    //public static bool QueueUserWorkItem(WaitCallback callBack, object state);
    //public static bool QueueUserWorkItem(WaitCallback callBack);
    //QueueUserWorkItem参数为回调函数WaitCallBack,该委托类型定义如下:public delegate void WaitCallback(object state);
    object obj = "ThreadPool demo obj";
    ThreadPool.QueueUserWorkItem(DoWork,obj);
    Console.ReadKey();

    //System.Threading.Timer
    //一种定时器工具,用来在后台线程定期计划执行任务
    //Timer类型构造函数的参数是回调函数TimerCallback: public delegate void TimerCallback(object state);
    Timer Mytimer = new Timer(DoWork, "MyTimer", 0, 200);//每200马上执行一次回调函数DoWork
    Mytimer.Change(0, 1000);//更新timer的interval参数200->1000ms.
    Console.ReadKey();
}

static void DoWork(object obj)
{
    Console.WriteLine($"{obj}开始执行工作线程任务");
    Thread.Sleep(200);//模拟工作线程任务
    Console.WriteLine("...");
    Thread.Sleep(200);//模拟工作线程任务
    Console.WriteLine("......");
    Thread.Sleep(200);//模拟工作线程任务
    Console.WriteLine(".........");
    Console.WriteLine("..........");
    Thread.Sleep(200);//模拟工作线程任务
    Console.WriteLine("............");
    Thread.Sleep(200);//模拟工作线程任务
    Console.WriteLine("...............");
    Console.WriteLine($"{obj}工作线程任务结束");
}

执行结果如下

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值