C#之多线程(下)

一、线程的死锁问题

1)线程的“死锁”与"同步"问题

死锁:多个线程,可能由于争抢一个“公共资源”,造成一些异常情况

解决方法,可以使用Lock关键字,让多个线程,顺序访问公共资源,这个技术也叫线程的“同步”

2)线程“同步”是线程中非常重要的概念,所谓同步是指线程之间存在的先后执行顺序的关联关系

//深入学习多线程
//学习多线程的“死锁”与解决方式“同步”
//演示线程的“死锁”测试类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;//多线程命名空间
public class TestDemo{
    private int _GameState=100;//游戏状态
    
    //private object objLock=new object();//定义一把“锁”,解决“死锁”问题
    public void ChangeState(){
        while(true){
            //使用lock锁住、套住会出现死锁的方法,则会解决“死锁”问题
            //lock(objLock){
            ChangeMyState();//死循环
        //}   
        }
    }
    private void ChangeMyState(){
        ++_GameState;
        if(_GameState==100){
            Console.WriteLine("GameState=100,会输出本语句吗?");
        }
        _GameState=100;
    }
}
//调用测试类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;//多线程命名空间
class Demo{
    public Test(){
        Console.WriteLine("测试与实验多线程的死锁问题");
        TestDemo obj=new TestDemo();
        Thread t1=new Thread(obj.ChangeState);//如果只定义一个线程,则只会输出一次结束
        t1.Start();
        Thread t2=new Thread(obj.ChangeState);//定义了第二个线程,则永不会停止,一直输出
        t2.Start();
        //因为两个线程不分先后,会互相抢先交叉运行,抢夺资源,不断输出,这就是“死锁”
    }
    static void Main(string[] args){
        Demo obj=new Demo();
        obj.Test();
    }
}

 线程同步问题实例二:

//进一步演示多线程“死锁”
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;//多线程命名空间
class TestDemo{
    private int _Num=0;
    //private object objLock=new object();//定义一把“锁”,必须是引用类型,解决“死锁”问题
    public void Add(){
        while(true){
            //使用lock锁住、套住会出现死锁的方法,则会解决“死锁”问题
            //lock(objLock){
            _Num++;
            Thread.Sleep(1000);
            Console.WriteLine(Thread.CurrentThread.Name+":"+_Num);
            //}
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;//多线程命名空间
class Demo{
    public void Test(){
        Console.WriteLine("测试多线程死锁问题,演示二");
        TestDemo obj=new TestDemo();
        Thread t1=new Thread(obj.Add);
        t1.Name="TA";
        t1.Start();
        Thread t2=new Thread(obj.Add);
        t2.Name="TB";
        t2.Start();
    }
    static void Main(string[] args){
        Demo obj=new Demo();
        obj.Test();
    }
}

二、线程池

1)为什么使用“线程池”

创建线程需要一定的时间,为了提高效率可以事先创建一些线程

2)系统有一个ThreadPool类,去管理这些线程。最大线程数量是可以配置的,最大线程可以达到1023个工作线程,1000个IO线程

3)线程池默认都是后台线程,不能把池中的线程修改为前台线程,也不能修改线程中优先级与名称

4)线程池中的线程只能用于时间比较短的任务。(如果后台线程需要长时间执行,则需要单独开启,不适合用线程池)

//学习“线程池”
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;//多线程命名空间
class Demo{
    public void ThreadMethod(object para){
        //Thread.CurrentThread.ManagedThreadId当前线程编号
        Console.WriteLine("开始下载(当前线程编号)"+Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(3000);
        Console.WriteLine("下载完毕");
    }
    public void Test(){
        ThreadPool.QueueUserWorkItem(ThreadMethod,123);
        ThreadPool.QueueUserWorkItem(ThreadMethod,123);
        ThreadPool.QueueUserWorkItem(ThreadMethod,123);
        ThreadPool.QueueUserWorkItem(ThreadMethod,123);
        ThreadPool.QueueUserWorkItem(ThreadMethod,123);
        Console.ReadLine();//线程池中都是后台线程,必须使用ReadLine,否则后台线程会因为前台线程的结束而直接结束
    }
    static void Main(string[] args){
        Demo obj=new Demo();
        obj.Test();
    }
}

三、任务

1)为什么使用“任务”

ThreadRool.QueueUserWorkItem()这个技术存在许多限制。其中最大的问题是没有一个内建的机制让你知道操作在什么时候完成,也没有一个机制在操作完成是获得一个返回值,这些问题使得我们都不敢启用这个技术。

通过任务,可以指定在(一个)任务完成之后,应开始运行之后另一个特定任务。例如,一个使用前一个任务结果的新任务,如果前一个任务失败了,这个任务就应该执行一些清理工作。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;//任务命名空间
using System.Threading;//多线程命名空间
class Demo{
    public void ThreadMethod(){
        Console.WriteLine("开始下载");
        Thread.Sleep(2000);
        Console.WriteLine("下载完毕");
    }
    public void test(){
        Task t=new Task(ThreadMethod);
        t.Start();
        Console.ReadLine();//线程池与任务开启的线程都是后台线程。
    }
    static void Main(string[] args){
        Demo obj=new Demo();
        obj.Test();
    }
}

2)“连续任务”,如果一个任务,是需要其他任务的完成为前提条件,则称为“连续任务”

3)任务的层次结构

在一个任务中,又启动了一个新的任务,相当于新的任务是本任务的子任务。两个任务异步执行,如果父任务执行完了,但是子任务没有执行完,他的状态会设置为WaitingForChildrenToComplete,只有子任务也执行完毕了,父任务的状态才变为RunToCompletion。

4)任务与线程的区别

任务可以认为是线程的封装

任务有“父子”关系,线程没有

四、总结

1)开启线程有四种方式

通过Thread开启

通过委托开启

通过线程池开启

通过线程任务开启

2)补充:任务工厂

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;//多线程命名空间
class Demo{
     public void ThreadMethod(){
        Console.WriteLine("开始下载");
        Thread.Sleep(2000);
        Console.WriteLine("下载完毕");
    }
    public void Test(){
        TaskFactory ft=new TaskFactory();
        Task t=ft.StartNew(ThreadMethod);
        Console.ReadLine();
    }
    static void Main(string[] args){
        Demo obj=new Demo();
        obj.Test();
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_49251429/article/details/123744507