C# 中Thread、ThreadPool、Task的使用

一、理论铺垫

什么是线程

1、举个例子,就拿我喜欢的LOL来说:
我们打游戏时,经常会按Tab键,显示计分板来查看双方的战绩,对方出了什么装备,进行针对性的出装。这个计分板就是一个线程,在不断地更新各自的信息。这个计分板并不影响我们打游戏。
各位骚年们,打游戏也要有梦想!
2、再举个例子,当我打开Word输入文字的时候,左下角会显示我们输入了多少字。其中计算输入了多少文字就是一个线程,在后台运行。我们可以不用管它,继续编辑文档。

前台线程跟后台线程

1、举个例子,还是拿LOL来说:
在游戏中,我们通过鼠标右击来使英雄移动。我是ad,想去拿个红Buff,我右击小地图红Buff所在的位置,英雄便自动地行走,这时,我可以查看计分板,这个计分板就是个后台线程,它不影响我的英雄移动。
如果,计分板一直打开,等待关闭后,英雄才开始往红Buff所在的位置移动,那这个计分板是个前台线程,它阻塞了其他线程的运行。
现在ad的地位是越来越尴尬了。。。
2、再举个例子:比如,网页中有某个按钮(这个按钮的功能是点击后,会在页面上显示额外的信息),如果我们点击了这个按钮,要等半天,并且不能进行其他操作,比如点击其他的按钮,这个就是前台线程;如果我们点击这个按钮,虽然要等半天,但我们仍可以进行其他操作,看看这个页面的其他部分,这个就是后台线程。

线程池

个人的理解:线程池嘛,就是一个池子,里面包含了很多待使用的线程。它的核心就是不需要自己每次都新建一个线程,比如某个线程执行完毕后
,不会自行销毁,而是以挂起的状态返回线程池里,等待新任务。
这么说的话,优点与劣势自然就出来了:
优点:
1、使用起来简单,只需使用QueueUserWorkItem来调用方法即可
2、减少了线程的开销(不需要自己手动新建与销毁线程),如果通过Thread新建一个线程的话,开销就差不多要消耗1M左右的内存
缺点:
1、它其中的一个优点,不需要自己手动新建与销毁线程,有时候也成了缺点,不好控制各个线程的执行,不好判断线程什么时候执行完了,那怎么办呢,这时,Task便出现了。

Task

Task其实就是在ThreadPool的基础上进行一层封装,ThreaPool启动的线程不好判断线程的执行情况,但Task可以,很好地解决了这个问题。

二、使用

备注:构造函数、属性、方法介绍常用到的。
通过Thread创建的线程默认为前台线程,当然也可以修改;通过ThreadPool创建的线程只能为后台线程。

Thread类

创建和控制线程,设置其优先级并获取其状态。

构造函数:

public Thread (System.Threading.ThreadStart start);无参数
public Thread (System.Threading.ParameterizedThreadStart start);有参数

属性:

IsBackground获取或设置线程是否为后台线程
Priority获取或设置优先级
ManagedThreadId获取当前线程的唯一标识符

方法:

Abort()终止线程
Join()让线程依次运行(这个方法经常用到)

使用:

//无参数的线程
Thread thread=new Thread(new ThreadStart(方法名));//实例化线程
thread.Start();//启动线程

//有参数的线程
Thread threadParam = new Thread(new ParameterizedThreadStart(方法名));//有参数
//这里有个非常重要的知识
方法里面的形参必须是object类型的
threadParam.Start(DateTime.Now);//有参数的线程启动方法

ThreadPool

提供一个线程池,该线程池可用于执行任务、发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。

方法:

QueueUserWorkItem(WaitCallback)将方法排入队列以便执行
QueueUserWorkItem(WaitCallback, Object)将方法排入队列以便执行,Object需要传递的参数

使用:

ThreadPool.QueueUserWorkItem(方法名);//这个方法必须要有个参数object

Task

表示一个异步操作。

构造函数:

public Task (Action action);无参数无返回值
public Task(Action action, object state);有参数无返回值
public Task(Func<object, TResult> function, object state);有参数有返回值

属性:

CurrentId正在执行的Task的id
IsCompleted是否完成
IsCompleted是否出现异常

方法:

Start()启动Task
Wait()等待Task执行完成

使用:

Task task_NoParam = new Task(无参数无返回值的方法);
task_NoParam.Start();

Task task_WithParam = new Task(有参数无返回值的方法, 传给方法的参数);
task_WithParam.Start();

Task<string> task_WithParam_WithReturn = new Task<string>(有参数有返回值的方法, 传给方法的参数);
task_WithParam_WithReturn.Start();
string Result=task_WithParam_WithReturn.Result;//返回的结果

三、具体代码

class CommonClass
    {
        public void TestMethod()
        {
            Console.WriteLine("没有参数的方法");
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("无参数的方法" + i + "");
            }
        }

        public void TestMethod(object obj)//这个形参必须是object类型的---这很重要
        {
            Console.WriteLine("有参数的方法,参数为" + obj.ToString() + "");
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("有参数的方法" + i + "");
            }
        }
        public void TestMethod_ThreadPool(object obj)
        {
            if (obj != null)
            {
                Console.WriteLine("ThreadPool-有参数的方法,参数为" + obj.ToString() + "");
                for (int i = 0; i < 3; i++)
                {
                    Console.WriteLine("ThreadPool-有参数的方法" + i + "");
                }
            }
            else
            {
                Console.WriteLine("ThreadPool-没有参数的方法");
                for (int i = 0; i < 3; i++)
                {
                    Console.WriteLine("ThreadPool-无参数的方法" + i + "");
                }
            }

        }
        public void TestMethod_Task_NoParam()
        {
            Console.WriteLine("Task-无参数");
        }
        public void TestMethod_Task_WithParam(object obj)
        {
            Console.WriteLine($"Task-有参数,参数为:{obj.ToString()}");
        }
        public string TestMethod_Task_WithParam_WithReturn(object obj)
        {
            Console.WriteLine($"Task-有参数,参数为:{obj.ToString()}");
            return obj.ToString();
        }
    }

主程序代码:

class Program
    {
        static void Main()
        {
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("主线程" + i + "");
            }
            CommonClass commonClass = new CommonClass();


            //Thread的使用
            Thread thread = new Thread(new ThreadStart(commonClass.TestMethod));//没有参数
            //thread.IsBackground = false;//设置前台线程还是后台线程,在线程启动前设置
            thread.Start();//没有参数的线程启动方法

            Thread threadParam = new Thread(new ParameterizedThreadStart(commonClass.TestMethod));//有参数
            threadParam.Start(DateTime.Now);//有参数的线程启动方法

            //等待上面两个线程执行完后
            thread.Join();
            threadParam.Join();

            Console.WriteLine("\n下面是ThreadPool的使用");
            //ThreadPool的使用
            ThreadPool.QueueUserWorkItem(commonClass.TestMethod_ThreadPool);
            ThreadPool.QueueUserWorkItem(commonClass.TestMethod_ThreadPool, DateTime.Now);
            //注意:使用ThreadPool不好判断线程什么时候完成

            Thread.Sleep(1000);
            Console.WriteLine("\n下面是Task的使用");
            //Task的使用
            Task task_NoParam = new Task(commonClass.TestMethod_Task_NoParam);//无参数无返回值的方法
            task_NoParam.Start();
            Task.WaitAll(task_NoParam);//等task_NoParam这个Task执行完执行下面的
            //这就是使用Task的好处,便于控制,知道Task什么时候执行完
            //不像TreadPool,让他启动后台线程,然后就没有然后了。任务完成后自动销毁。
            Task task_WithParam = new Task(commonClass.TestMethod_Task_WithParam, "sdf");//有参数无返回值的方法
            task_WithParam.Start();
            Task<string> task_WithParam_WithReturn = new Task<string>(commonClass.TestMethod_Task_WithParam_WithReturn, "sfdgsdfgasdf");//有参数有返回值
            task_WithParam_WithReturn.Start();
            Console.WriteLine("有参数有返回值的Task执行结果:" + task_WithParam_WithReturn.Result + "");


            Console.ReadKey();
        }
    }

结果:
运行结果

四、总结

爬虫中,经常会用到多线程的。
希望这篇博客能够帮到大家!
理论上Task性能较好,但实际上ThreadPool性能最好,也有可能是我使用的方式不对。仅供参考!
各位骚年们,打游戏也是要有梦想滴!

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用C#编写的线程池类代码示例: ```csharp using System; using System.Threading; namespace ThreadPoolExample { class Program { static void Main(string[] args) { // 创建线程池对象 ThreadPoolExample threadPoolExample = new ThreadPoolExample(); // 启动线程池 threadPoolExample.Start(); Console.ReadKey(); } } public class ThreadPoolExample { private int _numberOfThreads = 4; // 线程池的线程数 private int _taskCount = 10; // 要执行的任务数 public void Start() { // 创建线程池 ThreadPool.SetMaxThreads(_numberOfThreads, _numberOfThreads); ThreadPool.SetMinThreads(2, 2); // 创建任务 for (int i = 0; i < _taskCount; i++) { ThreadPool.QueueUserWorkItem(new WaitCallback(ExecuteTask), i); } } private void ExecuteTask(object task) { // 模拟任务执行 Console.WriteLine($"Task {task} is executing on thread {Thread.CurrentThread.ManagedThreadId}"); Thread.Sleep(1000); Console.WriteLine($"Task {task} execution completed on thread {Thread.CurrentThread.ManagedThreadId}"); } } } ``` 在上面的示例,我们创建了一个 `ThreadPoolExample` 类,其包括一个 `Start()` 方法,该方法使用 `ThreadPool` 类来启动线程池并执行任务。 我们在 `Start()` 方法设置了线程池的最大和最小线程数。然后我们创建了 `_taskCount` 个任务,并将它们添加到线程池。每个任务都是通过调用 `ThreadPool.QueueUserWorkItem()` 方法来添加的。该方法需要一个 `WaitCallback` 委托,该委托指向要执行的方法。在这里,我们将 `ExecuteTask()` 方法作为要执行的方法。 `ExecuteTask()` 方法是模拟要执行的任务。我们将每个任务的执行延迟 1 秒钟,并在控制台打印出任务执行的线程 ID。 最后,我们在 `Main()` 方法实例化了 `ThreadPoolExample` 类并调用了 `Start()` 方法来启动线程池。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值