一、什么是多任务处理?
理解 C # 中多线程的概念之前,需要先理解一下多任务处理。顾名思义,多任务处理意味着同时执行多个任务。Windows操作系统是一个多任务操作系统。这意味着它能够同时运行多个应用程序。例如,在机器上可以同时打开谷歌浏览器、记事本、Visual Studio 等等。
二、操作系统如何同时执行多个应用程序?
为了同时执行多个应用程序(Chrome浏览器、word文档、记事本、Visual Studio 等) ,操作系统内部使用到了进程。
(一)进程是什么?
进程是操作系统(或操作系统下的组件)的一部分,负责执行程序或应用程序。因此,要执行每个程序或应用程序,都需要一个过程。打开电脑的任务管理器,就可以看到正在执行的应用程序。每个应用程序由一个对应的进程执行。同样,也有多个进程在后台运行,称为后台进程。这些后台进程称为 Windows 服务,操作系统在后台运行大量的 Windows 服务。
因此,在操作系统之下,我们有运行应用程序的进程。因此,需要记住的一点就是在每一个进程下面都运行一个对应的应用程序。而为了运行应用程序中的代码,进程内部使用一个名为 Thread 的概念。
(二)线程是什么?
通常,线程(Thread) 是一个轻量级进程。简单地讲,可以说线程是负责执行应用程序代码的进程的一个单元。因此,每个程序或应用程序都有一些逻辑或代码,为了执行这些逻辑或代码,线程就出现了。
默认情况下,每个进程至少有一个线程负责执行应用程序代码,该线程称为主线程。因此,默认情况下,每个应用程序都是单线程应用程序。C # 中所有与线程相关的类都在 System.Threading 命名空间下。
三、通过代码示例理解线程
新建控制台应用程序,编写如下代码:
namespace ThreadDemo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello world");
Console.ReadKey();
}
}
}
如何查看上述程序在运行时使用到了线程?可以通过visual studio查看,在最后一行代码打断点,调试启动项目,点击调试->窗口->线程,可以看到如下界面:
可以看到 Main Thread 当前正在执行 Program 类的 Main 方法,因此,这证明了默认情况下,每个应用程序都是单线程应用程序,默认情况下,主线程将执行我们的应用程序代码。
四、c# Thread类
按f12查看Thread类内部定义,如下:
public sealed class Thread : CriticalFinalizerObject
{
//
// 摘要:
// Gets the currently running thread.
//
// 返回结果:
// A System.Threading.Thread that is the representation of the currently running
// thread.
public static Thread CurrentThread { get; }
public string? Name { get; set; }
public static void Sleep(int millisecondsTimeout);
public static void Sleep(TimeSpan timeout);
// 其它方法和属性
}
可以看到它包含一个静态属性 CurrentThread,返回类型是 Thread,也就是说,它将返回当前正在执行的线程实例,也就是运应用程序代码的线程。同样,还有一个名为 Name 的非静态属性,可以使用该属性设置和获取当前正在执行的线程的名称。注意: 默认情况下,线程没有任何名称。如果需要,可以使用 Thread 类的 Name 属性为线程提供任何名称。 代码如下:
namespace ThreadDemo
{
internal class Program
{
static void Main(string[] args)
{
var t = Thread.CurrentThread;
t.Name = "Main Thread";
Console.WriteLine("Current Executing Thread Name :" + t.Name);
// output:Current Executing Thread Name :Main Thread
Console.WriteLine("Current Executing Thread Name :" + Thread.CurrentThread.Name);
// output:Current Executing Thread Name :Main Thread
Console.ReadKey();
}
}
}
通过输出结果可知,为了运行应用程序代码,创建了一个线程,即 Main Thread。因此,这证明了在默认情况下,每个应用程序都是单线程应用程序。
五、在.NET中单线程应用缺点是什么?
在单线程应用程序中,程序中存在的所有逻辑或代码将只由一个线程执行,即主线程。例如,如果我们的应用程序中有三个方法,并且这三个方法都将在 Main 方法调用。然后主线程会按顺序执行这三个方法,即一个接一个地执行。首先执行第一个方法,完成后再执行第二个,第三个以此类推。如下代码所示:
namespace ThreadDemo
{
internal class Program
{
static void Main(string[] args)
{
Method1();
Method2();
Method3();
Console.ReadKey();
}
static void Method1()
{
for (int i = 1; i <= 5; i++)
{
Console.Write($"Method1 :{i} ");
}
Console.WriteLine();
}
static void Method2()
{
for (int i = 1; i <= 5; i++)
{
Console.Write($"Method2 :{i} ");
}
Console.WriteLine();
}
static void Method3()
{
for (int i = 1; i <= 5; i++)
{
Console.Write($"Method3 :{i} ");
}
Console.WriteLine();
}
}
}
output:
Method1 :1 Method1 :2 Method1 :3 Method1 :4 Method1 :5
Method2 :1 Method2 :2 Method2 :3 Method2 :4 Method2 :5
Method3 :1 Method3 :2 Method3 :3 Method3 :4 Method3 :5
可以看到,三个方法被一个接一个地调用和执行。主线程首先执行 Method1,完成后就调用 Method2,然后调用 Method3。
(一)这样执行有什么问题?
在以上示例中,只是编写一些简单的代码来输出数值。如果一个方法花费的时间超过了预期的时间,就会给客户带来不好的体验,假设 Method2将与数据库交互,或者它将调用第三方 Web 服务,可能需要超过10秒的时间来提供响应。在这种情况下,Method2的执行将延迟10秒钟,因为它正在那里等待从数据库、 Web 服务获得响应。在 Method2没有完成之前,Method3不会被执行,因为程序是由一个主线程按顺序执行的,即一个接一个地执行。
(二)如何解决上述问题?
为了解决上述问题,在 C # 中提供了一个称为多线程的概念。正如文章开始所讨论,操作系统具有用于运行我们的应用程序的进程。进程中包含实际运行我们的应用程序代码的线程。
默认情况下,进程使用单个线程运行应用程序代码,但是一个进程可以有多个线程,并且多个线程可以同时运行应用程序中的代码。在这种情况下,每个线程将执行不同的任务,或者可以说每个线程将执行应用程序代码的不同部分。
在以上代码中定义了三个方法。因此,三个方法可以让三个不同的线程执行。这样做的好处是执行同时进行。因此,当多个线程试图执行应用程序代码时,操作系统会为每个线程分配一段时间来执行。修改代码如下:
namespace ThreadDemo
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main Thread Started");
//Creating Threads
var t1 = new Thread(Method1)
{
Name = "Thread1"
};
var t2 = new Thread(Method2)
{
Name = "Thread2"
};
var t3 = new Thread(Method3)
{
Name = "Thread3"
};
//Executing the methods
t1.Start();
t2.Start();
t3.Start();
Console.WriteLine("Main Thread Ended");
Console.ReadKey();
}
static void Method1()
{
for (int i = 1; i <= 5; i++)
{
Console.Write($"Method1 :{i} ");
}
Console.WriteLine();
}
static void Method2()
{
for (int i = 1; i <= 5; i++)
{
if (i == 3)
{
Console.WriteLine("Connection database,Getting data...");
//Sleep for 10 seconds
Thread.Sleep(10000);
Console.WriteLine("Database Operation Completed");
}
Console.Write($"Method2 :{i} ");
}
Console.WriteLine();
}
static void Method3()
{
for (int i = 1; i <= 5; i++)
{
Console.Write($"Method3 :{i} ");
}
Console.WriteLine();
}
}
}
在上面的代码中看到的,创建了 Thread 类的三个不同实例。对于 Thread 类的构造函数,需要传递需要由该 Thread 执行的方法名。然后我们调用 Thread 类的 Start ()方法,该方法将开始执行构造函数里边方法。输出结果是完全没有顺序的,如下:
Main Thread Started
Main Thread Ended
Method2 :1 Method2 :2 Method3 :1 Method3 :2 Method3 :3 Method3 :4 Method3 :5 Method1 :1 Method1 :2 Method1 :3 Method1 :4 Method1 :5
Connection database,Getting data...
Database Operation Completed
Method2 :3 Method2 :4 Method2 :5
通过visual studio 打开线程窗口,可以看到 thread1 thread2 thread3三个工作线程:
如果使用多个线程来执行应用程序代码,则称为多线程处理。多线程是一种实现并发编程的机制,有多个线程同时操作,来执行对应的代码块。线程是轻量级进程,在程序中就类似执行路径。线程使用提高了应用程序的效率,并减少了 CPU 周期时间浪费。使用多线程的主要优点是最大限度地利用了 CPU 资源。在 c # 中主要使用Thread类来帮助我们实现多线程功能,Thread类具体是什么?
- Thread 类用于创建线程。
- 在 Thread 类的帮助下,我们可以创建前台线程和后台线程。
- Thread 类还允许我们设置线程的优先级。
- Thread 类还提供了线程的当前状态。
- C # 中的 Thread 类是一个密封类,因此无法继承。