- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
要深刻理解什么是线程,就需要了解计算机的发展史,需要了解多任务概念,需要了解进程概念,然后才是线程概念。因为我们主要还是讲解线程,因此这里就不进行展开说其他概念了,有兴趣的可以自行了解下.
简单来说,线程就是操作系统中能够单独执行任务的最小单元.
对于大多数编程语言来说,都有一个或者类似的功能的Main()方法,而该方法中的所有代码也都是按照顺序一行一行的执行,如果要想执行下一行代码那么就必须等待上一行代码执行完成。而线程作为能够单独执行任务的最小单元,因此线程可以使得应用程序的一部分独立于另外一部分而单独运行,这也就意味着我们可以改变程序的常规代码执行顺序,从而达到更复杂的程序控制.
对于一个应用程序来说,要想正常运行至少要有一个线程,通常为主线程,也就是上文中提到的Main()方法,当调用此方法时系统就会自动创建一个主线程.
线程可以分为前台线程和后台线程,两者基本完全相同,唯一区别是前台线程可以在托管执行环境中一直运行,而后台线程不可以。简单来说就是当进程中所有前台进程都停止后,系统会自动停止并关闭该进程内的所有后台线程.
在C#中可以通过Thread.IsBackground查看当前线程类型,也可以通过该属性修改线程类型。默认情况下,通过Thread对象新建并启动的所有线程都是前台线程.
看如下简单示例:
public static void CreateThread()
{
Console.WriteLine($"主线程 是否为后台线程:{Thread.CurrentThread.IsBackground}");
var thread1 = new Thread(()=> Console.WriteLine("Hello World"));
Console.WriteLine($" 线程1 默认为后台线程:{thread1.IsBackground}");
thread1.IsBackground = true;
Console.WriteLine($" 线程1 设置为后台线程:{thread1.IsBackground}");
thread1.Start();
}
执行效果如下:
线程作为操作系统中能够单独执行任务的最小单元,那么当一个进程中有多个线程时,应该先执行那个线程呢?因此线程需要一个标记其执行优先级的属性.
在C#中Thread可以通过Priority来设置线程的优先级,告诉系统应该先执行谁。ThreadPriority有以下5种类型:
下面我们做个简单的测试,用来验证优先级不同的导致差异.
class ThreadPriorityTest
{
//是否执行,确保一个线程修改此值后,其他线程立刻查看到最新值
static volatile bool isRun = true;
//确保每个线程都有独立的副本存储计数统计值
[ThreadStatic]
static long threadCount;
//停止运行
public void Stop()
{
isRun = false;
}
//打印线程名称对应优先级以及计数总数
public void Print()
{
threadCount = 0;
while (isRun)
{
threadCount++;
}
Console.WriteLine($"{Thread.CurrentThread.Name} 优先级为{Thread.CurrentThread.Priority,8} 总执行计数为:{threadCount,-13:N0}");
}
}
public static void PriorityTest()
{
var threadPriorityTest = new ThreadPriorityTest();
//创建3个线程,并设置优先级
var thread1 = new Thread(threadPriorityTest.Print)
{
Name = "线程1"
};
var thread2 = new Thread(threadPriorityTest.Print)
{
Name = "线程2",
Priority = ThreadPriority.Lowest
};
var thread3 = new Thread(threadPriorityTest.Print)
{
Name = "线程3",
Priority = ThreadPriority.Highest
};
//启动3个线程
thread1.Start();
thread2.Start();
thread3.Start();
//休眠3秒
Thread.Sleep(10000);
//停止运行
threadPriorityTest.Stop();
//等待所有线程完成
thread1.Join();
thread2.Join();
thread3.Join();
}
执行效果如下:
可以发现优先级越高,其执行计数值越大.
其中需要注意的是volatile和ThreadStatic的用法.
在这个多线程示例中我们需要准确的统计不同的线程执行计数,因此正常来说可能需要设置多个变量用来对应存储各自线程的统计计数,很显然这样会导致代码臃肿。因此我们选用了另一种办法,使用ThreadStatic标记一个字段,使得该字段对每个线程都有独立的副本。这样可以做到线程之间不会共享这个字段的值,同时还可以做到多个线程只用这一个字段.
另外对于多线程共享的变量,很可能因为CPU缓存导致多个线程共享的变量不一致问题,因此通过volatile告诉编译器和运行时每次访问该字段时都要直接从内存中读取最新值,以此来保证线程之间的可见性.
当一个线程被创建后,会经历多个状态,包括未启动、已启动、执行、睡眠、挂起等十个状态,同时Thread类也提供了一些方法,用来控制当前线程状态,比如启动、停止、恢复、中止、挂起以及等待线程等方法.
我们先来看看线程具体有哪些状态:
下面我们再来看看线程的常用方法.
通过源码可以看到Resume和Suspend方法被弃用的原因。这是因为它们有很多问题和缺陷。使用它可能会导致程序的不稳定、死锁或者资源竞争问题。因此,它已经被标记为废弃,不推荐再使用.
在多线程编程中,通常可以通过合理的同步机制来控制线程的执行。比如,使用上述的 Monitor、Mutex、Event 和 Semaphore 来协调多个线程的行为,确保资源访问的安全和正确性.
注:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Planner 。
最后此篇关于并发编程-初识线程的文章就讲到这里了,如果你想了解更多关于并发编程-初识线程的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我是一名优秀的程序员,十分优秀!