一.线程常用概念
线程(Thread):操作系统执行程序的最小单位
进程(Process):程序在内存中的运行实例
并发(Concurrency):多个任务交替执行(单核CPU)
并行(Parallelism):多个任务同时执行(多核CPU)
同步(Synchronization):协调线程执行顺序
异步(Asynchronous):非阻塞的执行方式
二.使用多线程好处
- 提高性能:利用多核CPU并行执行任务
- 响应性:保持UI界面流畅,后台执行耗时操作
- 资源利用率:同时处理I/O密集型和CPU密集型任务
- 模块化:将复杂任务分解为独立执行的单元
三.使用Thread实现多线程
常用Thread中为ThreadStart,其中ThreadStart为委托类型,无参数无返回值委托
//Thread线程
public Thread(ThreadStart start)
//ThreadStart委托类型,为无参数无返回值类型
public delegate void ThreadStart();
定义一个无参数无返回值的函数,使用线程进行操作
static void Main(string[] args)
{
//委托执行
Thread t1 = new Thread(run);
t1.Start();
//匿名函数
new Thread(()=>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"线程B==>{i}");
}
}).Start();
Console.WriteLine( "void Main执行完毕");
}
public static void run()
{ for(int i = 0; i < 10; i++)
{
Console.WriteLine($"线程A==>{i}");
}
}
打印结果:
void Main执行完毕
线程A==>0
线程A==>1
线程A==>2
线程A==>3
线程A==>4
线程A==>5
线程A==>6
线程A==>7
线程A==>8
线程A==>9
线程B==>0
线程B==>1
线程B==>2
线程B==>3
线程B==>4
线程B==>5
线程B==>6
线程B==>7
线程B==>8
线程B==>9
线程特性常用的属性获取,thread ID,name,IsAlive,IsBackground,使用线程A/B和主线程打印1-10的数字,其中IsAlive线程在运行中为True,线程在运行后为False,IsBackground指线程是否是后台线程,在程序执行过程中,前台线程执行完毕后,应用程序可以退出,后台线程则无所谓执行完成,应用程序均可以退出。默认情况下,Thread设定线程均是前台线程。
static void Main(string[] args)
{
Thread t1 = new Thread(run);
t1.Name = "ThreadA";
t1.Start();
Thread t2 = new Thread(() =>
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine($"线程ID:{Thread.CurrentThread.ManagedThreadId},线程名字{Thread.CurrentThread.Name}执行==>{i}");
}
}
);
t2.Name = "ThreadB";
t2.Start();
Thread.CurrentThread.Name = "Main Thread";
for (int i = 0; i < 3; i++)
{
Console.WriteLine($"线程ID:{Thread.CurrentThread.ManagedThreadId},线程名字{Thread.CurrentThread.Name}执行==>{i}");
}
Console.WriteLine("void Main执行完毕");
}
public static void run()
{ for(int i = 0; i < 3; i++)
{
Console.WriteLine($"线程ID:{Thread.CurrentThread.ManagedThreadId},线程名字{Thread.CurrentThread.Name}执行==>{i}");
}
}
打印结果,每次打印结果会有差异:
线程ID:3,线程名字ThreadA执行==>0
线程ID:3,线程名字ThreadA执行==>1
线程ID:3,线程名字ThreadA执行==>2
线程ID:1,线程名字Main Thread执行==>0
线程ID:4,线程名字ThreadB执行==>0
线程ID:4,线程名字ThreadB执行==>1
线程ID:4,线程名字ThreadB执行==>2
线程ID:1,线程名字Main Thread执行==>1
线程ID:1,线程名字Main Thread执行==>2
void Main执行完毕
线程调度:
t1.Priority=ThreadPriority.Highest; //线程优先级
bool a=Thread.Yield(); //线程礼让
Thread.Sleep(1000); //线程休眠
t1.Join(); //线程阻塞
线程安全:使用线程模拟12306抢票系统,由于线程是按照同步进行,如果对线程不进行约束,则会出现数据重复现象,用以下代码进行:
internal class Program
{
public int TotalTicket;
public int CurrentTicket;
static void Main(string[] args)
{
Program program = new Program();
program.TotalTicket = 5;
program.CurrentTicket = 5;
Thread t1 = new Thread(program.GetTicket);
t1.Name = "黄牛";
t1.Start();
Thread t2 = new Thread(program.GetTicket);
t2.Name = "小王";
t2.Start();
Thread t3 = new Thread(program.GetTicket);
t3.Name = "小李";
t3.Start();
}
public void GetTicket()
{ while (true)
{
if (CurrentTicket <= 0)
{
break;
}
//有票
CurrentTicket--;
Thread.Sleep(100);
Console.WriteLine( $"{Thread.CurrentThread.Name}抢到了{TotalTicket-CurrentTicket}张票,剩余{CurrentTicket}张票");
}
}
运行结果,可见线程运行过程中使用同一数据元,由于电脑分配资源执行过程中间隔执行,数据源不安全:
小王抢到了3张票,剩余2张票
小李抢到了3张票,剩余2张票
黄牛抢到了3张票,剩余2张票
小王抢到了5张票,剩余0张票
小李抢到了5张票,剩余0张票
解决这个问题,使用线程锁,即当前线程执行完毕后再进行执行第二个线程,使用Lock,Lock是 C# 中的一种用于同步线程执行的机制,它帮助确保多个线程在访问共享资源时不会发生冲突或数据损坏。其作用是通过给临界区(即多线程访问共享资源的代码段)加锁,使得在同一时刻只能有一个线程进入执行该代码段。:
internal class Program
{
public int TotalTicket;
public int CurrentTicket;
private readonly object o = new object(); //设定一个对象
static void Main(string[] args)
{
Program program = new Program();
program.TotalTicket = 5;
program.CurrentTicket = 5;
Thread t1 = new Thread(program.GetTicket);
t1.Name = "黄牛";
t1.Start();
Thread t2 = new Thread(program.GetTicket);
t2.Name = "小王";
t2.Start();
Thread t3 = new Thread(program.GetTicket);
t3.Name = "小李";
t3.Start();
}
//利用Lock对线程进行互斥锁定
public void GetTicket()
{ while (true)
{
lock(o)
{
if (CurrentTicket <= 0)
{
break;
}
//有票
CurrentTicket--;
Thread.Sleep(100);
Console.WriteLine($"{Thread.CurrentThread.Name}抢到了{TotalTicket - CurrentTicket}张票,剩余{CurrentTicket}张票");
}
}
}
打印结果,同一资源则不会进行数据丢失:
小李抢到了1张票,剩余4张票
黄牛抢到了2张票,剩余3张票
小王抢到了3张票,剩余2张票
小李抢到了4张票,剩余1张票
黄牛抢到了5张票,剩余0张票