随着.net版本不断升级,目前多种多线程实现方法

一 .Thread    最基本

1.优缺点

优点--Thread API丰富

缺点--   1.线程资源是操作系统管理的,对API响应并不灵敏,(也就是调用一次提供的API可能不会立即响应)难以控制
    2.线程启动数量是没有控制的,可能会导致死机等意外发生

2.Thread对象实例化方法(四种)

  2.1声明一个无参的、返回值为void的委托ThreadStart,委托内含一个静态方法;

  2.2 声明一个无参的、返回值为void的委托ThreadStart,委托内含一个对象方法;

  2.3 直接使用匿名委托;

  2.4 直接使用Lambda表达式;

 

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"主线程,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Thread thread1 = new Thread(new ThreadStart(NewThreadDisplay));//第一种

            AnotherObject another = new AnotherObject();
            Thread thread2 = new Thread(new ThreadStart(another.AnotherThreadDisplay));//第二种

            Thread thread3 = new Thread(delegate() { Console.WriteLine($"子线程3,ThreadId: {Thread.CurrentThread.ManagedThreadId}"); } );//第三种
            Thread thread4 = new Thread(()=> Console.WriteLine($"子线程4,ThreadId: {Thread.CurrentThread.ManagedThreadId}"));//第四种

            thread1.Start();//启动子线程1
            thread2.Start();//启动子线程2
            thread3.Start();//启动子线程3
            thread4.Start();//启动子线程4
            Console.ReadKey();
        }

    static void NewThreadDisplay()
        {
            Console.WriteLine($"子线程1,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
        }
    }
    class AnotherObject
    {
        public void AnotherThreadDisplay()
        {
            Console.WriteLine($"子线程2,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
        }
    }
}

输出结果:

主线程,ThreadId: 1
子线程3,ThreadId: 5
子线程4,ThreadId: 6
子线程1,ThreadId: 3
子线程2,ThreadId: 4

 可以看出多线程启用时无序的,即使一个线程在是先start的,执行也可能在后start的线程后面;并且每次执行的先后顺序都不一定相同

3.关于线程优先级 Priority属性
Priority属性是一个ThreadPriority型枚举,列举了5个优先等级:Highest、AboveNormal、Normal(普通线程默认)、BelowNormal、Lowest

4.关于线程的状态
通过ThreadState可以检测线程是处于Unstarted、Sleeping、Running 等等状态,它比 IsAlive 属性能提供更多的特定信息。
通过CurrentContext可以获取线程当前的上下文。
CurrentThread是最常用的一个属性,它是用于获取当前运行的线程。

5.列举一些API(可能有坑)
终止-Abort() GetDomain()-返回当前线程正在其中运行的当前域 GetDomainId()   Interrupt()-中断处于   WaitSleepJoin 线程状态的线程

Join() - 已重载。 阻塞调用线程,直到某个线程终止时为止    Resume()-继续运行已挂起的线程    Start()-执行本线程   Suspend()-挂起当前线程   

Sleep()- 把正在运行的线程挂起一段时间

6.关于前台线程和后台线程

前台线程:只有所有的前台线程都结束,应用程序才能结束。默认情况下创建的线程都是前台线程
后台线程:只要所有的前台线程结束,后台线程自动结束。通过Thread.IsBackground设置后台线程。必须在调用Start方法之前设置线程的类型,否则一旦线程运行,将无法改变其类型。
通过BeginXXX方法运行的线程都是后台线程。
注1:后台线程一般用于处理不重要的事情,应用程序结束时,后台线程是否执行完成对整个应用程序没有影响。如果要执行的事情很重要,需要将线程设置为前台线程。

二. ThreadPool

.NetFramework 2.0 新增
基于池化资源管理设计思想,线程是一种资源,每次要用线程,就去申请一个线程,使用完释放;池化就是一种容器,容器提前申请5个线程,程序需要使用线程直接找容器获取,

用完再放回(空置状态,避免频繁申请和销毁,容易还会根据限制的数量去申请和释放)
优点:1线程复用 2限制最大线程数量
缺点:API太少了,在线程顺序控制上弱,用起来不方便

三.Task--.NetFramework 3.0 新增

比较常用的多线程方式,全部是线程池线程、提供丰富API

Task线程全部是线程池线程、提供丰富API

Action action = o=>{xxxx;}
Task task =new Task(action);
task.Start();

举例:

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        { 
            Console.WriteLine($"主线程开始,ThreadId: {Thread.CurrentThread.ManagedThreadId}");

            Task task1 = new Task(() => Console.WriteLine($"子线程1,ThreadId: {Thread.CurrentThread.ManagedThreadId}"));
            task1.Start();

            //也可以直接创建并启用
            List<Task> taskList = new List<Task>();
            taskList.Add(Task.Run(() => Console.WriteLine($"子线程2,ThreadId: {Thread.CurrentThread.ManagedThreadId}")));
            taskList.Add(Task.Run(() => Console.WriteLine($"子线程3,ThreadId: {Thread.CurrentThread.ManagedThreadId}")));
            taskList.Add(Task.Run(() => Console.WriteLine($"子线程4,ThreadId: {Thread.CurrentThread.ManagedThreadId}")));

            Task.WaitAny(taskList.ToArray());////阻塞当前线程,直到任一任务结束
            Console.WriteLine($"有一个子线程执行完毕,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Task.WaitAll(taskList.ToArray());////阻塞当前线程,直到全部任务结束
            Console.WriteLine($"所有子线程执行完毕,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Console.WriteLine($"主线程结束,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Console.ReadKey();
        }

    }
}

执行结果:

主线程开始,ThreadId: 1
子线程1,ThreadId: 4
子线程2,ThreadId: 7
子线程3,ThreadId: 8
子线程4,ThreadId: 4
有一个子线程执行完毕,ThreadId: 1//阻塞了主线程执行!
所有子线程执行完毕,ThreadId: 1////阻塞了主线程执行!
主线程结束,ThreadId: 1

通过结果发现,无论怎么执行,最后三行输出的顺序都是一样的!可见通过waitAny()、waitAll()方法可以阻塞主线程的执行,已经等达到一些顺序控制的目的了!

注:1.尽量不要线程套线程,有更优秀的方法
2.子线程不能直接操作界面
等全部任务完成后启动一个新的task完成后续动作
TaskFactory taskFactory =new TaskFactory();
taskFactory.ContinueWhenAll(taskList.ToArray(),tArray=>{
xxxx
})
等任一任务完成后启动一个新的task完成后续动作
taskFactory.ContinueWhenAny(taskList.ToArray(),tArray=>{
xxxx
})
//continue的后续线程,不可能是主线程,其余皆可能

四.Parallel并行编程
主线程也参与计算,介于线程,节约一个线程
通过指定ParallelOptions控制最大并发数量
Parallel.Invoke(()=>{111;},()=>{222;},()=>{333;})

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        { 
            Console.WriteLine($"主线程开始,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Parallel.Invoke(new ParallelOptions() { MaxDegreeOfParallelism = 4},
                              () => {
                                  Console.WriteLine($"子线程1,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                Thread.Sleep(1000); },
                              () => {
                                  Console.WriteLine($"子线程2,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                Thread.Sleep(1000);},
                              () => {
                                  Console.WriteLine($"子线程3,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                  Thread.Sleep(1000);
                              },
                              () => {
                                  Console.WriteLine($"子线程4,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                  Thread.Sleep(1000);
                              },
                              () => {
                                  Console.WriteLine($"子线程5,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                  Thread.Sleep(1000);
                              });
            Console.WriteLine($"主线程结束,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Console.ReadKey();
        }

    }
}

输出结果:

主线程开始,ThreadId: 1
子线程1,ThreadId: 1
子线程2,ThreadId: 3
子线程3,ThreadId: 5
子线程4,ThreadId: 4
子线程5,ThreadId: 1
主线程结束,ThreadId: 1

特点:主线程(1)也参与计算,节约一个线程;可以通过ParallelOptions控制最大并发数量(本例设置为4),子线程5与线程1使用了同一个线程,所以5等1执行完了之后才会打印,同理主线程结束也在最后完成;

五、BeginInvoke()线程封装相关的

委托.BeginInvoke()自动启用异步多线程,并且带 回调 BeginInvoke()

3.利用委托方式在子线程中改变主线程UI的内容

private delegate void delegateUpdateLabel(string text);
    private void UpdateLabel(string text)
        {
            if (this.updateProcess_label.InvokeRequired)
            {
                Invoke(new delegateUpdateLabel(UpdateLabel), new object[] { text });
            }
            else
            {
                updateProcess_label.Text = text;
            }
        }
    IAsyncResult asyncResult1 = action.BeginInvoke("", null, null);    
    IAsyncResult asyncResult2 = func.BeginInvoke(null, null);

判断异步线程完成方式:

1.利用asyncResult.IsCompleted 2.利用信号量asyncResult.AsyncWaitHandle.WaitOne(),可以做超时判断 ---waitOne(-1)一直等待 waitOne(100)等待100ms
带返回值的异步调用方法 returnValue = func.EndInvoke(asyncResult2) EndInvoke()也能用在回调里,但只能用一次