多线程之旅(Thread)
在上篇文章中我们已经知道了多线程是什么了,那么它到底可以干嘛呢?这里特别声明一个前面的委托没看的同学可以到上上上篇博文查看,因为多线程要经常使用到委托。源码
一、异步、同步
1.同步(在计算的理解总是要你措不及防,同步当线程做完一件事情之后,才会执行后续动作),同步方法慢,只有一个线程执行,异步方法快,因为多个线程一起干活,但是两者并不是线性增长,当我们的异步线程占有的资源越来越多了,会导致资源可能不够,其次线程过多CPU也是需要管理成本的,所以不是越多越好。
2.异步(可以同时执行多个任务,在同样的时间,执行不同的任务),同步方法卡界面(UI),因为我们的主线程(UI)忙于计算造成了堵塞了。异步方法不卡界面,计算任务交给了子线程完成。winform中体现的玲玲精致。(你品,你细品),web 可以异步的处理一起其他的任务,比如给用户发邮箱(我们的BS结构的,每次访问都是一个子线程,当我们的代码写的比较糟糕,是不是加载比较慢呢哈哈)。异步多线程无序,执行的先后无序,执行的时间不确定,结束也不确定,所以我们很难通过执行时间和先后顺序控制,异步的执行顺序。
二、初识Thread
| 属性名称 | 说明 |
|---|---|
| CurrentContext | 获取线程正在其中执行的当前上下文。 |
| CurrentThread | 获取当前正在运行的线程。 |
| ExecutionContext | 获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。 |
| IsAlive | 获取一个值,该值指示当前线程的执行状态。 |
| IsBackground | 获取或设置一个值,该值指示某个线程是否为后台线程。 |
| IsThreadPoolThread | 获取一个值,该值指示线程是否属于托管线程池。 |
| ManagedThreadId | 获取当前托管线程的唯一标识符。 |
| Name | 获取或设置线程的名称。 |
| Priority | 获取或设置一个值,该值指示线程的调度优先级。 |
| ThreadState | 获取一个值,该值包含当前线程的状态。 |
Thread 中包括了多个方法来控制线程的创建、挂起、停止、销毁,后面的例子中会经常使用。
| 方法名称 | 说明 |
|---|---|
| Abort() | 终止本线程。 |
| GetDomain() | 返回当前线程正在其中运行的当前域。 |
| GetDomainId() | 返回当前线程正在其中运行的当前域Id。 |
| Interrupt() | 中断处于 WaitSleepJoin 线程状态的线程。 |
| Join() | 已重载。 阻塞调用线程,直到某个线程终止时为止。 |
| Resume() | 继续运行已挂起的线程。 |
| Start() | 执行本线程。 |
| Suspend() | 挂起当前线程,如果当前线程已属于挂起状态则此不起作用 |
| Sleep() | 把正在运行的线程挂起一段时间。 |
1.Thread是我们.NET 1.0 给我们提供的多线程类,可以创建,和控制多线程,Thread类构造函数为接受ThreadStart和ParameterizedThreadStart类型的委托参数,下面有请代码神君。
/// <summary>
/// 使用Thread 创建多线程
/// </summary>
public static void Show()
{
//实例化创建线程 无参无返回值
Thread thread = new Thread(() =>
{
Console.WriteLine("我是多线程");
});
thread.Start(); //创建5个线程1
for (int i = ; i < ; i++)
{
//这个之所以创建一个k,后面线程不安全会说到
var k = i;
//这是一个有参数无返回值多线程
new Thread(x => Running(Convert.ToInt32(x))).Start(k);
}
Console.Read();
} /// <summary>
/// 一个执行需要长时间的任务
/// </summary>
static void Running(int s)
{
Console.WriteLine("**********************************");
Console.WriteLine("执行开始啦" + s);
Console.WriteLine("获取当前执行的线程ID:" + Thread.CurrentThread.ManagedThreadId.ToString());
var j = ;
for (int i = ; i < ; i++)
{
j++;
}
Console.WriteLine("执行结束啦" + s);
}

二、渐入佳境
1.运行上面的代码,可以看到线程的无序性,虽然我们的0最先开始执行的,但是不是第一个结束的,这个是因为我们每个线程执行的时间的不确定性。这里也要特别说明为什么Thread构造函数传递的是ThreadStart和ParameterizedThreadStart类型的委托参数,为什么不是Action ,Func,答案就是.NET 1.0的时候还没有Action 、Func。ThreadStart委托是一个无参无返回值上代码中我们创建了,ParameterizedThreadStart委托是一个有参数无返回值,但是我们可以看到我们的参数是一个object类型,是一个不安全的参数(当时泛型也没有出来)当然为了防止这问题,我们也是想到了方法,那就是我们可以通过一个泛型类,帮我们限制参数类型。
/// <summary>
/// 防止参数不安全
/// </summary>
public static void Show5()
{
//我们创建一个泛型类,限制我们的类型
MyThread<string> mythread = new MyThread<string>("Thread_child");
//将我们的方法传递,进去
Thread th3 = new Thread(mythread.ThreadChild);
//启动线程
th3.Start();
} /// <summary>
/// 创建一个泛型类
/// </summary>
/// <typeparam name="T"></typeparam>
class MyThread<T>
{
private T data;
public MyThread(T data)
{
this.data = data;
}
public void ThreadChild()
{
Console.WriteLine("Child Thread Start! Result:{0}", data);
}
}
2.我们在上面还提供了其他的方法,但是这些方法已经不建议使用了,现在已经弃用了,因为我们无法精确地控制线程的开启与暂停,当我们将线程挂起的时候,同时也会挂起线程使用的资源,会导致死锁,不建议使用。将线程销毁也不建议 不一定及时/有些动作发出收不回来。(这里我使用的是.net Core 3.1 执行直接报错了哈哈)
/// <summary>
/// 使用Thread 线程挂起、唤醒线程、销毁,方式是抛异常、取消Abort异常
/// </summary>
public static void Show1()
{
//创建一个Thread 线程
Thread thread = new Thread(() =>
{
Running();
});
//开启线程
thread.Start();
//这个是线程挂起
//thread.Suspend();
//唤醒线程
//thread.Resume();
//上面的两个方法,现在已经弃用了,因为我们无法精确地控制线程的开启与暂停
//当我们将线程挂起的时候,同时也会挂起线程使用的资源,会导致死锁,不建议使用
try
{
//将线程销毁
//也不建议 不一定及时/有些动作发出收不回来
thread.Abort();
}
catch (Exception)
{
//静态方法将线程异常取消继续工作
Thread.ResetAbort();
}
Console.Read();
}

3.线程优先级,当然我们的线程是一个无序的,也有控制线程执行的权重,但是这个优先级不是绝对的,因为线程的执行顺序还是看我们的CPU爸爸的,但是我们可以利用Priority属性做线程的权重执行,使用也很简单
/// <summary>
/// 使用Thread 线程的优先级(但是执行还是看CPU,可以做优先级,但是不是绝对优先)
/// </summary>
public static void Show3()
{
//创建一个Thread 线程
Thread thread = new Thread(() =>
{
Running();
});
thread.Start();
//thread.Priority属性可以设置线程的优先级关系
thread.Priority = ThreadPriority.Highest;
Console.WriteLine("执行完啦啦啦啦啦啦啦啦啦啦啦拉拉");
Console.Read();
}
4.前台线程、后台线程(这个字面意思,还是和我们的理解是不一样的)我们设置IsBackground控制线程是否(前/后)台线程。默认是前台线程,启动之后一定要完成任务的,阻止进程退出。指定后台线程:随着进程退出。

三、多线程起飞
1、异步回调
1.我们的Thread没有给我提供异步回调的功能,没办法需要自己造轮子了,我们可以先想一下回调的需求是什么,需求分析:当我们的线程任务执行完之后需要之后某些方法。我们细品一下,我们要执行完之后,在执行一个人任务,那就是同步执行异步方法了吧。我们在子线程中怎么同步执行呢?下面的代码就实现了回调功能不管我们执行多少次回调总会在任务后面执行。
/// <summary>
/// 异步回调执行
/// </summary>
public static void Show6() {
//创建一个任务委托
ThreadStart threadStart = () => {
Console.WriteLine("我是任务");
};
//创建一个回调执行的委托
Action action = () => {
Console.WriteLine("哈哈,我就是你们的回调方法哈,记得双击么么哒");
Console.WriteLine("*********************************************");
};
ThreadWithCallback(threadStart, action);
Console.ReadLine();
} /// <summary>
/// 回调封装 无返回值
/// </summary>
/// <param name="start"></param>
/// <param name="callback">回调</param>
private static void ThreadWithCallback(ThreadStart start, Action callback)
{
Thread thread = new Thread(() =>
{
start.Invoke();
callback.Invoke();
});
thread.Start();
}

2、返回参数
1.当然我们使用线程需要返回参数,但是我们的Thread没有给我们提供返回值的委托和方法,这个要莫子搞罗?当然我们先分析需求,我们要获取返回值是不是要等线程执行之后呢?好的线程执行我们可以使用Join堵塞线程等它执行完毕,但是我们要怎么获取返回值呢?对了我们可以创建一个变量,我们的线程给变量赋值吗?
/// <summary>
/// 异步返回值
/// </summary>
public static void Show7()
{
//创建一个委托
Func<string> func = () => {
return "我是返回值";
};
//获取执行结果
Console.WriteLine(ThreadWithReturn(func).Invoke());
Console.ReadLine();
} /// <summary>
/// 有返回值封装(请根据本案例自行封装回调)
/// </summary>
/// <typeparam name="T">返回值类型</typeparam>
/// <param name="func">需要子线程执行的方法</param>
/// <returns></returns>
private static Func<T> ThreadWithReturn<T>(Func<T> func)
{
//初始化一个泛型,限制我们的类型
T t = default(T);
ThreadStart newStart = () =>
{
//线程给变量赋值
t = func.Invoke();
};
//创建线程
Thread thread = new Thread(newStart);
//执行线程
thread.Start();
//创建一个委托 无参有返回值,执行委托会发生执行线程等待堵塞
//当线程执行完之后,也就是说线程已经给变量t赋值了,我们就返回t
return new Func<T>(() =>
{
thread.Join();
return t;
});
}

四、Thread总结
1.大家是不是觉得多线程很酷呢?哈哈我刚刚学的时候也是激动的心颤抖的手。当然文章中我们介绍了很多API的使用,大家可以动手试试,API的使用是小事,最重要的是我们的思路,到我们看到回调封装和返回值封装,我们都是利用了多线程的一些特性,来完成的这些功能拓展的。我们宏观的看多线程感觉很恐怖,但是在我们做回调函数的时候是不是感觉有一种微观看法,线程执行的内部也是同步的执行哪些方法的。好了今天就写到这里昨天晚上9点多就睡了,早起撸个文章美滋滋。当然多线程还有讲完的,才说道了.NET 1.0哈哈,后续的文章也会写出来。
多线程之旅(Thread)的更多相关文章
- C#多线程之旅(3)——线程池
v博客前言 先交代下背景,写<C#多线程之旅>这个系列文章主要是因为以下几个原因:1.多线程在C/S和B/S架构中用得是非常多的;2.而且多线程的使用是非常复杂的,如果没有用好,容易造成很 ...
- C#多线程之旅(1)——介绍和基本概念
原文地址:C#多线程之旅(1)——介绍和基本概念 C#多线程之旅目录: C#多线程之旅(1)——介绍和基本概念 C#多线程之旅(2)——创建和开始线程 C#多线程之旅(3)——线程池 C#多线程之旅( ...
- C#多线程之旅(7)——终止线程
先交代下背景,写<C#多线程之旅>这个系列文章主要是因为以下几个原因:1.多线程在C/S和B/S架构中用得是非常多的;2.而且多线程的使用是非常复杂的,如果没有用好,容易造成很多问题. ...
- C#多线程之旅(4)——APM初探
源码地址:https://github.com/Jackson0714/Threads 原文地址:C#多线程之旅(4)——APM初探 C#多线程之旅目录: C#多线程之旅(1)——介绍和基本概念 C# ...
- Java多线程4:Thread中的静态方法
一.Thread类中的静态方法 Thread类中的静态方法是通过Thread.方法名来调用的,那么问题来了,这个Thread指的是哪个Thread,是所在位置对应的那个Thread嘛?通过下面的例子可 ...
- “全栈2019”Java多线程第二章:创建多线程之继承Thread类
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- 2018.3.3 多线程中继承Thread 和实现Runnable接口 的比较(通过售票案例来分析)
多线程中继承Thread 和实现Runnable接口 的比较(通过售票案例来分析) 通过Thread来实现 Test.java package com.lanqiao.demo4; public cl ...
- 【C#多线程】1.Thread类的使用及注意要点
Thread随便讲讲 因为在C#中,Thread类在我们的新业务上并不常用了(因为创建一个新线程要比直接从线程池拿线程更加耗费资源),并且在.NET4.0后新增了Task类即Async与await关键 ...
- Java多线程专题3: Thread和ThreadLocal
合集目录 Java多线程专题3: Thread和ThreadLocal 进程, 线程, 协程的区别 进程 Process 进程提供了执行一个程序所需要的所有资源, 一个进程的资源包括虚拟的地址空间, ...
随机推荐
- CoreGraphic
public func UIGraphicsBeginImageContextWithOptions( size: CGSize, opaque: Bool, _ scale: CGFloat) si ...
- android 应用程序与服务端交互
http://www.cnblogs.com/freeliver54/archive/2012/06/13/2547765.html 简述了Service的一些基础知识以及Service和Thread ...
- 初学Qt——tableview操作
先做简短记录改天有空再详细讲一些吧 使用QSqlQueryModel绑定了TableView,因为需要用到数据表中Id这一项,但又不想显示出来,找到方法是 在绑定之后对tableView 调用setC ...
- web博客
欢迎大家来戳一戳
- Rust入坑指南:朝生暮死
今天想和大家一起把我们之前挖的坑再刨深一些.在Java中,一个对象能存活多久全靠JVM来决定,程序员并不需要去关心对象的生命周期,但是在Rust中就大不相同,一个对象从生到死我们都需要掌握的很清楚. ...
- k8s环境部署.net core web项目(docker本地仓库)
在之前的文档中,我们部署了.net core web在k8s环境下,达成了集群管理项目的目的.但是,由于是本地部署,需要在所有的node节点都拉取好镜像,这是非常麻烦的,为了只维护一份代码,同步更新. ...
- psql的jsonb操作--存储对象/对象数组
1. 建表 create table demo( id serial NOT NULL PRIMARY KEY, name ), info JSONB ); 2.存储对象操作 2.1添加 insert ...
- Yuchuan_Linux_C编程之九目录操作相关函数
一.整体大纲 二.相关函数 1. getcwd 函数作用:获取当前目录 头文件 #include <unistd.h> 函数原型 char *getcwd(char *buf, size_ ...
- linux构建DHCP服务器
1.DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个局域网的网络协议,使用UDP协议工作,主要用途:给内部网络或网络服务供应商自动分配IP地址 ...
- Docker Compose + Traefik v2 快速安装, 自动申请SSL证书 http转https 初次尝试
前言 昨晚闲得无聊睡不着觉,拿起服务器尝试部署了一下Docker + Traefik v2.1.6 ,以下是一些配置的总结,初次接触,大佬勿喷. 我的系统环境是 Ubuntu 18.04.3 LTS ...