C# 线程与任务
线程
线程:对于所有需要等待的操作,例如移动文件,数据库和网络访问都需要一定的时间,此时就可以启动一个新的线程,同时完成其他任务。一个进程的多个线程可以同时运行在不同的CPU上或多核CPU的不同内核上。
一个应用程序启动时,会启动一个进程(应用程序的载体),然后进程会启动多个线程。
一,使用Thread类启动线程和数据传输
使用Thread类可以创建和控制线程,Thread构造函数是一个无参无返回值的委托类型。
1️⃣对Thread传入一个无参无返回类型的方法-ThreadStart。
public delegate void ThreadStart();
实例:
static void test()
{
Console.WriteLine("test is start");
Console.WriteLine("test is running");
Thread.Sleep(3000);
Console.WriteLine("test is completed");
} static void Main(string[] args)
{
Thread th = new Thread(test);
th.Start();
Console.WriteLine("main is completed");
}

2️⃣对Thread传入一个匿名方法(或lambda表达式)。用于传入的方法代码简单的情况
static void Main(string[] args)
{
//lambad表达式
Thread th = new Thread(()=> {
Console.WriteLine("子线程1-ID:" + Thread.CurrentThread.ManagedThreadId);
});
th.Start();
//匿名方法
Thread th2 = new Thread(delegate ()
{
Console.WriteLine("子线程2-ID:" + Thread.CurrentThread.ManagedThreadId);
});
th2.Start();
Console.WriteLine("main is completed");
}

3️⃣对Thread传入一个无返回值有参方法-ParameterizedThreadStart,该参数只能是object类型且只能有一个参数。
public delegate void ParameterizedThreadStart(object? obj);
实例:
static void download(object o)
{
string str = o as string;
Console.WriteLine("地址:" + str);
}
static void Main(string[] args)
{
Thread th = new Thread(download);
th.Start("http://baidu.com");
Console.WriteLine("main is completed");
}

注意:使用as进行强制类型转换 成功会正确输出,失败会输出null。
以上数据传输的方法都是基于静态变量进行传输的,但是定义过多静态变量会导致多个线程访问同一个静态变量,造成资源冲突。
静态变量虽然方便访问,但是静态的一般都是公共的,容易混乱。
4️⃣对Thread传入一个无返回值多个参数的方法(定义一个结构体),但是不能用as强制转换。
public struct data
{
public string message;
public int age;
}
static void download(object o)
{
data str = (data)o;//强制类型转换
Console.WriteLine("信息:" + str.message);
Console.WriteLine("年纪:" + str.age);
}
static void Main(string[] args)
{
data da = new data();
da.message = "sss";
da.age = 3;
Thread th = new Thread(download);
th.Start(da);
Console.WriteLine("main is completed");
}
由于结构体是值类型,不能为null,因此不能用as进行强制类型转换。
5️⃣通过自定义类传递数据(即将通过线程调用一个类的成员方法)
先创建一个download类:
class downLoad
{
public string URL { get; private set; }
public string Message { get; private set; }
//构造函数
public downLoad(string uRL, string message)
{
URL = uRL;
Message = message;
}
//下载方法
public void load()
{
Console.WriteLine("从" + URL + "获取信息:" + Message);
}
}
在主程序中将该类的成员方法传入Thread中:
static void Main(string[] args)
{
var download = new downLoad("www.baidu.com", "mp4");
Thread th = new Thread(download.load);
th.Start();
Console.WriteLine("main is completed");
Console.ReadKey();
}
知识点拓展1-前台线程与后台线程:
应用程序的进程需要等待所有前台线程完成其任务后才会结束。而后台线程在应用程序关闭后会自动关闭,即使后台线程还没有执行完毕。在默认情况下,用Thread类创建的线程是前台线程,线程池中的线程是后台线程。在Thread类创建线程的时候,可以设置IsBackground属性,表示它是否是一个后台线程。
知识点拓展2-线程的优先级
线程有操作系统调度,一个CPU同一时间只能做一件事(运行一个线程中的计算任务),当有很多线程需要CPU执行时,线程调度器会根据线程的优先级去判断先去执行哪个线程,如果优先级相同,就使用一个循环调度规则,逐个执行每个线程。
在Thread类中,可以设置Priority属性,以影响线程的基本优先级,Priority属性一个ThreadPriority枚举定义的一个值,定义级别有Highest,AboveNormal,Normal,BelowNormal,和Lowest。
因此对于重要的线程任务,可以将线程优先级设置高一点,使其可以尽快执行完毕。
如果需要等待线程执行结果在执行后面的代码,可以调用Thread对象的join方法,即将该线程加入进来,并停止当前线程,直至加入的线程执行完毕。
二,线程池ThreadPool类
由于线程的创建需要时间,如果有不同的小任务要完成,就可以事先创建多个线程。系统有一个ThreadPool类来管理线程,这个类会在需要线程的时候增加线程数,不需要时候减少。池中最大线程数是可配置的。在双核CPU中,默认设置为1023个工作线程和1000个IO线程,如果需要更多线程(超过了线程池的最大数量),最新的任务就需要排队等待。
使用线程池,即调用ThreadPool.QueueUserWorkItem方法,该方法需要传入一个WaitCallBack类型的委托(即传入带一个object参数的方法)。然后ThreadPool会在池中找一个空闲的线程去执行传入的方法。
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(work);
}
Thread.Sleep(10000);
}
static void work(object state)
{
Console.WriteLine("线程id" + Thread.CurrentThread.ManagedThreadId);
}

需要注意的是:
- 线程池中所有线程都是后台线程,如果进程中所有的前台线程都结束了,所有的后台线程也会跟着结束。不能把入池的后台线程改为前台线程。
- 不能给入池的线程设置优先级或名称。
- 入池的线程只能是用于时间较短的任务。如果线程要一直运行,就应用Thread类创建一个线程。
任务
任务表示应完成某个单元的工作,这个工作可以在单独的线程中运行,也可以同步方式启动一个任务。任务在后台使用ThreadPool进行管理的,也就是说任务启动的也是后台线程。
一,创建并启动任务
启动任务的两种方式:
static void Main(string[] args)
{
//第一种:使用TaskFactory
TaskFactory tf = new TaskFactory();
tf.StartNew(work);
//第二种:使用Task
Task t = new Task(work);
t.Start();
Console.WriteLine("main is completed");
}
static void work()
{
Console.WriteLine("线程id" + Thread.CurrentThread.ManagedThreadId);
}

需要注意的是:使用TaskFactory创建任务,传入的方法为无参的。
二,连续任务
如果一个任务t1的执行是依赖于另一个任务t2,那么就需要在t2执行完毕后才开始执行t1。(例如:迅雷下载完成后弹出界面提示)这时候我们可以使用连续任务ContinueWith。
static void Main(string[] args)
{
Task t1 = new Task(download);
Task t2 = t1.ContinueWith(show);
t1.Start();
Thread.Sleep(2000);
}
static void download()
{
Console.WriteLine("正在下载中。。。。");
Thread.Sleep(1000);
}
static void show(Task t)
{
Console.WriteLine("下载完成!");
}

需要注意的是:传入t2的方法的参数需为Task类型。
三,资源冲突问题
在多线程中如果多个线程同时访问同一资源,就会产生资源冲突的问题。这时候需要用lock对程序进行加锁。
什么是资源冲突?
class state
{
public int num = 5;
public void checknum()
{
if (num==5)
{
num++;
Console.WriteLine("num的值:" + num + "当前线程id:" + Thread.CurrentThread.ManagedThreadId);
}
num =5;
}
}
static void Main(string[] args)
{
state st = new state();
for (int i = 0; i < 10; i++)
{
Thread th = new Thread(st.checknum);
th.Start();
}
}
在主程序使用多线程调用state类的check方法,可以看到num=5和num=6造成资源冲突了。

对多个线程访问的对象进行加锁
object _lock = new object();
public int num = 5;
public void checknum()
{
lock(_lock)
{
if (num==5)
{
num++;
Console.WriteLine("num的值:" + num + "当前线程id:" + Thread.CurrentThread.ManagedThreadId);
}
num =5;
}
}

C# 线程与任务的更多相关文章
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- [高并发]Java高并发编程系列开山篇--线程实现
Java是最早开始有并发的语言之一,再过去传统多任务的模式下,人们发现很难解决一些更为复杂的问题,这个时候我们就有了并发. 引用 多线程比多任务更加有挑战.多线程是在同一个程序内部并行执行,因此会对相 ...
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- Java 线程
线程:线程是进程的组成部分,一个进程可以拥有多个线程,而一个线程必须拥有一个父进程.线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不能拥有系统资源.它与父进程的其他线程共享该进程的所有资 ...
- C++实现线程安全的单例模式
在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式.单例模式分为懒汉模式,跟饿汉模式两种. 首先给出饿汉模式的实现 template <class T> class sing ...
- 记一次tomcat线程创建异常调优:unable to create new native thread
测试在进行一次性能测试的时候发现并发300个请求时出现了下面的异常: HTTP Status 500 - Handler processing failed; nested exception is ...
- Android线程管理之ThreadLocal理解及应用场景
前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...
- C#多线程之线程池篇3
在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...
- C#多线程之线程池篇2
在上一篇C#多线程之线程池篇1中,我们主要学习了如何在线程池中调用委托以及如何在线程池中执行异步操作,在这篇中,我们将学习线程池和并行度.实现取消选项的相关知识. 三.线程池和并行度 在这一小节中,我 ...
- C#多线程之线程池篇1
在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...
随机推荐
- WebLogic11g- 集群化
其实网上关于weblogic集群的资料非常多[大部分都是从创建新的domain开始,我这篇先介绍怎么样把原本普通的domain改造为集群环境],如果觉得不够,可以啃weblogic的官方文档.下面给几 ...
- ssh一段时间无操作后自动断开连接(假死)问题
平时使用ssh远程服务器的时候(注:远程虚拟机一般不会有这个问题),一段时间没有操作,ssh连接就会处于假死状态,以至于需要重新进行ssh连接,不管你用的什么远程工具都会出现这个问题,那么通过心跳检测 ...
- SpringBoot实现QQ邮件发送
建项目 创建一个SpringBoot项目 改pom,导入相关依赖 org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE & ...
- NSSCTF-no_wakeup
打开网页是一个派萌的表情包(原神玩家手动狗头) 按照题目的提示点击,出现题目的源码, 观察题目源码,发现就是一个简单的反序列化,这边手打一下php (自己太菜了,枯了) <?phpclass H ...
- RainbowCrack彩虹表破解密码hash
实验目的 使用彩虹表破解散列值b0baee9d279d34fa1dfd71aadb908c3f 实验原理 1)彩虹表破解是利用彩虹表破解散列数据的工具. 这种方法不同于暴力破解攻击.暴力破解攻击会将密 ...
- Renix修改报文长度——网络测试仪实操
Renix软件修改报文长度的方式有4种,分别是固定.递增.随机和自动.接下来对这四种方式,分别配置和验证. 一.固定(fixed) 描述:流中的帧具有固定长度 1.配置fixed 64Byte 2.w ...
- 国内主流大数据厂商太多不知道怎么选?Smartbi满足你的需求
目前的BI市场上有很多大数据的厂商,这也让许多客户眼花缭乱,不知如何下手.比如国内的亿信华辰跟国外的Tableau,QLK, PowerBI 都有非常多便于分析的可视化工具,但是在数据处理分析的功能 ...
- 为什么在数据驱动的路上,AB 实验值得信赖?
在线AB实验成为当今互联网公司中必不可少的数据驱动的工具,很多公司把自己的应用来做一次AB实验作为数据驱动的试金石. 文 | 松宝 来自 字节跳动数据平台团队增长平台 在线AB实验成为当今互联网公司中 ...
- Spring源码之六-onRefresh()方法
Spring源码之六-onRefresh()方法 大家好,我是程序员田同学. 今天带大家解读Spirng源码之六的onRefresh()方法,这是refresh()的其中的一个方法,看似是一个空方法, ...
- yaml文件学习笔记
01 介绍 YAML 是 "YAML Ain't Markup Language" 的递归缩写.在开发的这种语言时,YAML 的意思其实是:"Yet Another Ma ...