c#多线程之线程基础
作者:農碼一生 发布时间:2022-10-06 20:34:44
一、简介
1.为了防止一个应用程序控制CPU而导致其他应用程序和操作系统本身永远被挂起这一可能情况,操作系统不得不使用某种方式将物理计算分割为一些虚拟的进程,并给予每个执行程序一定量的计算能力。此外操作系统必须始终能够优先访问CPU,并能调整不同程序访问CPU的优先级。线程正式这一慨念的实现。
2.多线程优缺点:
多线程优点:可以同时执行多个计算任务,有可能提高计算机的处理能力,使得计算机每秒能执行越来越多的命令
多线程缺点:消耗大量的操作系统资源。多个线程共享一个处理器将导致操作系统忙于管理这些线程,而无法运行程序。
二、创建线程
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(PrintNumbers));//无参数的委托,把方法的引用当做参数
t1.Start();
Thread t2 = new Thread(new ParameterizedThreadStart(PrintNumbers));//有参数的委托,把方法的引用当做参数
t2.Start(10);
Console.ReadLine();
}
static void PrintNumbers()
{
Console.WriteLine("1.Starting...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
}
//注意:要使用ParameterizedThreadStart,定义的参数必须为object
static void PrintNumbers(object count)
{
Console.WriteLine("2.Starting...");
for (int i = 0; i < Convert.ToInt32(count); i++)
{
Console.WriteLine(i);
}
}
}
}
注释:
1.我们只需指定在不同线程运行的方法名,而C#编译器会在后台创建这些对象。
2.要使用ParameterizedThreadStart,定义的参数必须为object类型。
三、暂停线程
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(PrintNumbersWithDelay);
t1.Start();
PrintNumbers();
Console.ReadLine();
}
static void PrintNumbers()
{
Console.WriteLine("1.Starting...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine("In 1.Starting: " + i);
}
}
static void PrintNumbersWithDelay()
{
Console.WriteLine("2.Starting...");
for (int i = 0; i < 10; i++)
{
//var a = TimeSpan.FromSeconds(2);
Thread.Sleep(TimeSpan.FromSeconds(2));//暂停两秒
Console.WriteLine("In 2.Starting: " + i);
}
}
}
注释:使用Thread.Sleep(TimeSpan.FromSeconds(2));暂停线程一段时间
四、线程等待
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting...");
Thread t = new Thread(PrintNumbersWithDelay);
t.Start();
t.Join(); //使用Join等待t完成后,再向下执行PrintNumbers,如果注释掉输出明显不同
PrintNumbers();
Console.WriteLine("Thread Complete");
Console.ReadLine();
}
static void PrintNumbers()
{
Console.WriteLine("1.Starting...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine("In 1.Starting:" + i);
}
}
static void PrintNumbersWithDelay()
{
Console.WriteLine("2.Starting...");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("In 2.Starting:" + i);
}
}
}
注释:使用t.Join(); 等待t完成。
五、终止线程
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting Program...");
Thread t1 = new Thread(PrintNumbersWithDelay);
t1.Start();
Thread.Sleep(TimeSpan.FromSeconds(7));//此时t1线程会执行7秒
t1.Abort(); //使用Abort()终止线程
Console.WriteLine("Thread t1 has been aborted");
Thread t2 = new Thread(PrintNumbers);
t2.Start();
Console.ReadLine();
}
static void PrintNumbers()
{
Console.WriteLine("1.Starting...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine("In 1.Starting:" + i);
}
}
static void PrintNumbersWithDelay()
{
Console.WriteLine("2.Starting...");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("In 2.Starting:" + i);
}
}
}
注释:使用Thread实例的Abort方法终止线程。
六、检测线程状态
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start Program...");
Thread t1 = new Thread(PrintNumbersWithStatus);
Thread t2 = new Thread(DoNothing);
Console.WriteLine("t1 status:" + t1.ThreadState.ToString());//获取实例线程状态
t2.Start();
t1.Start();
for (int i = 0; i < 30; i++)
{
Console.WriteLine("t1 status:" + t1.ThreadState.ToString() + "\t" + "t2 status:" + t2.ThreadState.ToString());
}
Thread.Sleep(TimeSpan.FromSeconds(7));
t1.Abort();
Console.WriteLine("thread t1 has been aborted");
Console.WriteLine("t1 status:" + t1.ThreadState.ToString());
Console.WriteLine("t2 status:" + t2.ThreadState.ToString());
Console.ReadLine();
}
private static void PrintNumbersWithStatus()
{
Console.WriteLine("1.Starting...");
Console.WriteLine("In 1.Starting t1 status:" + Thread.CurrentThread.ThreadState.ToString());//获取当前线程状态
for (int i = 0; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("In 1.Starting:" + i);
}
}
private static void DoNothing()
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("t2 Console...");
}
}
注释:使用Thread.ThreadState获取线程的运行状态。ThreadState是一个C#枚举。谨记:不要在程序中使用线程终止,否则可能会出现意想不到的结果
七、线程优先级
class Program
{
static void Main(string[] args)
{
//让操作系统的所有线程运行在多个CPU核心上
Console.WriteLine($"Current thread priority: {Thread.CurrentThread.Priority}");
Console.WriteLine("Running on all cores available");//获取实例线程状态
RunThreads();
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("Running on a single Core");
//让操作系统的所有线程运行在单个CPU核心上
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
RunThreads();
Console.ReadLine();
}
private static void RunThreads()
{
var sample = new ThreadSample();
var t1 = new Thread(sample.CountNumbers);
t1.Name = "Thread One";
var t2 = new Thread(sample.CountNumbers);
t2.Name = "Thread Two";
t1.Priority = ThreadPriority.Highest;//使用Priority设置线程的优先级
t2.Priority = ThreadPriority.Lowest;
t1.Start();
t2.Start();//此处t2优先级低于t1,t2等待t1释放资源。
Thread.Sleep(TimeSpan.FromSeconds(2));
sample.Stop();
}
}
class ThreadSample
{
private bool _isStopped = false;
public void Stop()
{
_isStopped = true;
}
public void CountNumbers()
{
long counter = 0;
while (!_isStopped)
{
counter++;
}
Console.WriteLine($"{Thread.CurrentThread.Name} with {Thread.CurrentThread.Priority} priority has a count={counter.ToString("N0")}");
}
}
注释:单核执行多线程耗费的时间比多核的多很多。
八、前台线程和后台线程
class Program
{
static void Main(string[] args)
{
var sampleForground = new ThreadSample(10);
var sampleBackground = new ThreadSample(20);
var t1 = new Thread(sampleForground.CountNumbers);//方法的引用
t1.Name = "ForegroundThread"; //没有明确声明的均为前台线程
var t2 = new Thread(sampleBackground.CountNumbers);
t2.Name = "BackgroundThread";
t2.IsBackground = true; //设置为后台线程
t1.Start();
t2.Start();
Console.ReadLine();
}
}
class ThreadSample
{
private readonly int _iteration;
public ThreadSample(int iteration)
{
_iteration = iteration;
}
public void CountNumbers()
{
for (int i = 0; i < _iteration; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
}
}
}
注释:进程会等待所有的前台线程完成后再结束工作,但是如果只剩下后台线程,则会直接结束工作。
九、向线程传递参数
class Program
{
static void Main(string[] args)
{
ThreadSample sample = new ThreadSample(5);
Thread t1 = new Thread(sample.CountNumbers);
t1.Name = "ThreadOne";
t1.Start();
t1.Join();
Console.WriteLine("--------------------------");
Thread t2 = new Thread(Count);
t2.Name = "ThreadTwo";
t2.Start(3);
t2.Join();
Console.WriteLine("--------------------------");
//使用lambda表达式引用另一个C#对方的方式被称为闭包。当在lambda表达式中使用任何局部变量时,C#会生成一个类,并将该变量作为该类的一个属性,但是我们无须定义该类,C#编译器会自动帮我们实现
Thread t3 = new Thread(() => CountNumbers(5));
t3.Name = "ThreadThree";
t3.Start();
t3.Join();
Console.WriteLine("--------------------------");
int i = 10;
Thread t4 = new Thread(() => PrintNumber(i));
i = 20;
Thread t5 = new Thread(() => PrintNumber(i));
t4.Start();
t5.Start();
//t4, t5都会输出20, 因为t4,t5没有Start之前i已经变成20了
Console.ReadKey();
}
static void Count(object iterations)
{
CountNumbers((int)iterations);
}
static void CountNumbers(int iterations)
{
for (int i = 1; i <= iterations; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
}
}
static void PrintNumber(int number)
{
Console.WriteLine(number);
}
}
class ThreadSample
{
private readonly int _iteration;
public ThreadSample(int iteration)
{
_iteration = iteration;
}
public void CountNumbers()
{
for (int i = 1; i <= _iteration; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
}
}
}
十、使用C# Lock 关键字
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Incorrect Counter");
Counter c1 = new Counter();
var t1 = new Thread(() => TestCounter(c1));
var t2 = new Thread(() => TestCounter(c1));
var t3 = new Thread(() => TestCounter(c1));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine($"Total Count: {c1.Count}");
Console.WriteLine("------------------------");
//使用LOCK关键字,Count同一时刻只允许一个线程访问
Console.WriteLine("Correct counter");
CounterWithLock c2 = new CounterWithLock();
t1 = new Thread(() => TestCounter(c2));
t2 = new Thread(() => TestCounter(c2));
t3 = new Thread(() => TestCounter(c2));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine($"Total count:{c2.Count}");
Console.ReadLine();
}
static void TestCounter(CounterBase c)
{
for (int i = 0; i < 100000; i++)
{
c.Increment();
c.Decrement();
}
}
//子类
class Counter : CounterBase
{
public int Count { get; private set; }
//重写基类方法
public override void Decrement()
{
Count--;
}
public override void Increment()
{
Count++;
}
}
//子类
class CounterWithLock : CounterBase
{
private readonly object _asyncRoot = new object();
public int Count { get; private set; }
//重写基类方法
public override void Decrement()
{
lock (_asyncRoot)
{
Count--;
}
}
public override void Increment()
{
lock (_asyncRoot)
{
Count++;
}
}
}
//基类
abstract class CounterBase
{
public abstract void Increment();
public abstract void Decrement();
}
}
class ThreadSample
{
private readonly int _iteration;
public ThreadSample(int iteration)//构造函数
{
_iteration = iteration;
}
public void CountNumbers()
{
for (int i = 1; i <= _iteration; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
}
}
}
注释:不加锁,得出的结果不确定,竞争条件下很容易出错。加锁得出的结果是正确的,但是性能受到了影响
十一、使用Monitor类锁定资源
class Program
{
static void Main(string[] args)
{
object lock1 = new object();
object lock2 = new object();
new Thread(() => LockTooMuch(lock1, lock2)).Start();
lock (lock2)
{
Thread.Sleep(1000);
Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
//直接使用Monitor.TryEnter, 如果在第二个参数之前还未获取到lock保护的资源会返回false
if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
{
Console.WriteLine("Acquired a protected resource successfully");
}
else
{
Console.WriteLine("Timeout acquiring a resource");
}
}
new Thread(() => LockTooMuch(lock1, lock2)).Start();
Console.WriteLine("-----------------------------");
/* 下面代码会造成死锁, 所以注释掉
lock (lock2)
{
Console.WriteLine("This will be a deadlock!");
Thread.Sleep(1000);
lock (lock1)
{
Console.WriteLine("Acquired a protected resource successfully");
}
}
*/
}
static void LockTooMuch(object lock1, object lock2)
{
lock (lock1)
{
Thread.Sleep(1000);
lock (lock2);
}
}
}
注释:Monitor.TryEnter在指定的时间内尝试获取指定对象上的排他锁
十二、处理异常
class Program
{
static void Main(string[] args)
{
Thread t = new Thread(FaultyThread);
t.Start();
t.Join();
try
{
t = new Thread(BadFaultyThread);
t.Start();
}
catch (Exception ex)
{
Console.WriteLine("We won't get here");
}
}
static void BadFaultyThread()
{
Console.WriteLine("Starting a bad faulty thread.....");
Thread.Sleep(TimeSpan.FromSeconds(2));
//这个异常主线程无法捕捉到,因为是在子线程抛出的异常。需要在子线程中加入try...catch捕获异常
throw new Exception("Boom!");
}
static void FaultyThread()
{
try
{
Console.WriteLine("Starting a faulty thread...");
Thread.Sleep(TimeSpan.FromSeconds(1));
throw new Exception("Boom!");
}
catch (Exception ex)
{
Console.WriteLine($"Exception handled: {ex.Message}");
}
}
}
来源:https://www.cnblogs.com/wml-it/p/14808097.html
猜你喜欢
- 何时需要削峰当上游调用下游服务速率高于下游服务接口QPS时,那么如果不对调用速率进行控制,那么会发生很多失败请求通过消息队列的削峰方法有两种
- java 同步、异步、阻塞和非阻塞分析概要:正常情况下,我们的程序以同步非阻塞的方式在运行。但是我们的程序总会出现一些耗时操作,比如复杂的计
- 目录前言一、技术介绍1.ReentranReadWriteLock是什么?二、源码分析1.ReadLock2.WriteLock三、单元测试
- 日志是非常重要的,虽然他不会以需求功能提来,但也不会体现在产品方案中。但是,它在系统项目中却占有巨大的地位。为了保证服务的高可用,发现问题一
- 目录Spring自动注入失败如何解决?回答注入你的bean使用@Configurable手动查找bean:不推荐Spring自动注入失败如何
- 前言用户注册功能是每一个系统的入口门面功能,很多人可能会以为很简单,不就是一个简单的CRUD吗?其实不然,要把前后端功能都做出来,页面跳转也
- 安装Java:安装J2SE开发工具包5.0(JDK 5.0)下载:Java官方网站。请确保以下环境变量设置,如下所述:JAVA_HOME:
- 概述Thumbnailator 是一个开源的 Java 项目,它提供了非常简单的 API 来对图片进行缩放、旋转以及加水印的处理。有多简单呢
- Java持久化框架:Hibernate与MybatisHibernate和Mybatis是两个Java持久化框架,用于将Java对象映射到关
- Java IDE工具提供了多种用户独特需求和个人偏好来创建编程环境的方法。Java框架能够简化程序员的工作。这些框架被设计和开发用于在任何服
- 前言如果你了解过 Liunx ,了解过 Liunx 的中管道命令 | ,那么你会发现,其实 Java 8 的 stream 和 Liunx
- BeanPostProcessor 的接口定义,可以实现提供自己的实例化逻辑,依赖解析逻辑等,也可以以后在Spring容器实例化完毕,配置和
- 前言在实际生活中,地图是我们经常使用的一种工具,通常我们会用它进行导航,输入一个出发城市,输入一个目的地城市,就可以把路线规划好,而在规划好
- 按照官方文档进行的配置:快速开始|mybatis-plus引入依赖:<!-- 引入mybatisPlus --> &
- 库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。库分静态库和动态库两种。 一、静态库和动态库的区别1. 静态函数库这类库的
- 在一般性开发中,笔者经常看到很多同学在对待java并发开发模型中只会使用一些基础的方法。比如Volatile,synchronized。像L
- 一.介绍观察者模式(Observer Pattern)属于行为型模式。定义了对象之间的一对多依赖,让多个观察者同时监听某一个主题对象,类似于
- 什么是TKMybatisTKMybatis 是基于Mybatis 框架开发的一个工具,内部实现了对单表的基本数据操作,只需要简单继承 TKM
- 在分支较多的时候,switch的效率比if高,在反汇编中我们即可看到效率高的原因一、switch语句1、在正向编码时,switch语句可以看
- 本文实例讲述了Java实现打印二叉树所有路径的方法。分享给大家供大家参考,具体如下:问题:给一个二叉树,把所有的路径都打印出来。比如,对于下