天天看点

进一步了解多线程

任何的异步多线程,都是跟委托相关,没有委托,啥也没有

多线程1.0 1.1 Thread

Thread:是.Net框架封装的一个类,描述线程这个东西

同步方法:发起一个调用,一定要等着计算结束才运行下一行

异步方法:发起一个调用,不会等着计算结束,而是直接开始运行下一行

异步多线程:多线程说的 是CLR线程 异步IO线程

  1. 同步方法卡界面,以为ui线程忙于计算,异步多线程方法不卡界面,主线程闲置,计算任务交给子线程在做;
  2. 同步方法慢,只有一个线程计算,异步多线程方法快,多个线程并发计算;多线程的资源消耗更多,线程并不是越多越好(资源有限/管理线程也消耗资源)
  3. 异步多线程是无序的,启动无序 执行时间不确定 结束无序,所有我们不要试图通过顺序或者时间等待来控制流程

怎么控制顺序?

回调 等待 状态

AsyncCallback 回调

IsCompleted 状态

iAsyncResult.AsyncWaitHandle.WaitOne()//一直等待任务完成,第一时间进入下一行

iAsyncResult.AsyncWaitHandle.WaitOne(-1)//一直等待任务完成,第一时间进入下一行

iAsyncResult.AsyncWaitHandle.WaitOne(1000)//最多等待1000毫秒,否在进入下一行

Act.EndInvoke(iAsyncResult);//等待

String resultIn=func.EndInvoke(ar);//对于每个异步调用,只能调用一次 EndInvoke

ThreadStart threadStart = new ThreadStart(() =>
{
    Thread.Sleep(5000);
    this.DoSomethingLong("btnThreads_Click");//这是一个自己定义的私有方法
});
Thread thread = new Thread(threadStart);
thread.IsBackground = true;//变成后台线程
thread.Start();//默认是前台线程,UI线程退出后,还会继续执行完;后台线程就直接退出了
//thread.Suspend();//暂停
//thread.Resume();//恢复
//thread.Join();//做等待
//thread.Abort();//销毁
//while (thread.ThreadState != System.Threading.ThreadState.Running)
//{//线程是否还在运行,以可以起到等待的效果
//}
           

ThreadPool 线程池 2.0 封装

  1. 去掉各种 api 避免滥用,降低复杂度
  2. 池化:1减少创建/销毁的成本 2 限制最大线程数量
ThreadPool.QueueUserWorkItem(o=>
{
    Thread.Sleep(5000);
	//代码段
});
           

没有需求,就别等待,阻塞线程

ManualResetEvent 状态标

ManualResetEvent mre = new ManualResetEvent(false);

mre.WaitOne();//状态标为false时,不会过这个方法

mre.Set();//打开

mre.Reset();//关闭

ManualResetEvent mre = new ManualResetEvent(false);//false 关闭
                new Action(() =>
                {
                    Thread.Sleep(5000);
                    Console.WriteLine("委托的异步调用");
                    mre.Set();//打开
                    }).BeginInvoke(null, null);

                mre.WaitOne();
                Console.WriteLine("12345");
                mre.Reset();//关闭
                new Action(() =>
                {
                    Thread.Sleep(5000);
                    Console.WriteLine("委托的异步调用2");
                    mre.Set();//打开
                    }).BeginInvoke(null, null);
                mre.WaitOne();
                Console.WriteLine("23456");

           

Task 3.0

使用的是线程池的线程 全部是后台线程

API很强大

多线程:业务是可以并发执行

千万不要在Task里面去启动Task

启动:

Task.Run();

TaskFactory taskFactory=Task.Factory;//new TaskFactory();

taskFactory.StartNew();//启动

Task.WaitAny(taskList.toArray());//执行玩一个线程后开始执行

Task.WaitAll();//全部执行玩

//回调

taskFactory.ContinueWhenAll();

taskFactory.ContinueWhenAny();

4.0 Parallel

跟task很像 ==task+waitall 启动多个线程计算 主线程也参与计算,节约了一个线程

Parallel.Invoke();//
Parallel.For(0,5,t=>
{
	//代码块
	
})

Parallel.ForEach(new int[]{1,2,3,4,5},  options, (t,state)=>
{
	//代码块
	this.DoSomethingLog($”btnParallel_click_00{t}”);
	//state.Stop();//
	//state.Break();//
});

ParalleOptions options=new ParalleOptions()//同时在线 线程
{
	MaxDegreeOfParalleLism=3
};
           

ThreadCore 异常处理

多线程里的异常是会被丢掉,除非waitall

建议 多线程里面,是不允许异常,以就是内部try catch,自己处理

线程取消

线程取消不是操作线程,而是操作共享信号量(共享变量,多个线程都能访问到的东西,变量/数据库的数据/硬盘数据)

每个线程在执行的过程中,进程去超快下这个信号量,然后自己结束自己

线程不能别人终止,只能自己干掉自己,延迟是少不了的

CancellationTakensource cts = new CancellationTakensource(); //bool值

cts.IsCancllationRequested //检查信号

cts.Cancel();//

多线程临时变量

For(int i=0;i<5;i++){
Int k-i;
New Action(()=>
{
Thread.Sleep(100);
Console.Writeline(k);
Console.Writeline(i);
}).BeginInvoke(null,null);
}
           

i 只有一个,真实 实际的时候,已经是5了

k 有多个,每次都是独立的k,跟i没有关系

线程安全

公有变量:都能访问局部变量/全局变量/数据库的一个值/硬盘文件

线程内部不共享的是安全的

多个线程同时操作一个变量,变量的值可能被覆盖

解决多线程冲突第一个办法:lock的方法块里面是单线程 去掉多线程,lock里面的代码要尽量的少

Lock(){//lock后的方法块,任意时刻只有一个线程可以进入

}

解决多线程冲突第二个办法:没有冲突,从数据上隔离开

Lock==Monitor.Enter 检查下这个变量,有没有被lock 有就等着,没有就占有者,然后进去执行,执行玩释放

lock(this)锁定当前实例,别的地方如果要使用这个变量呢?都被锁定了

如果每个实例想要单独的锁定,private object

String a=”123456’ lock(a) string b=”123456” 享元模式的内存分配,字符串值是唯一的,会锁定的变量b

Private static readonly object btnThreadCore_Click_Lock=new object();

await/async C#5.0 .Net Framework4.5

await/async 是语法糖,本身是编译器提供的功能

async 用来修饰方法的

await 只能出现在Task 前面

await/async 本身不是线程

不推荐void返回值,使用Task来代替

Task和Task能够使用await,Task.WhenAny,Task.Whenall等方式组合使用,Async Void不行

无返回值

主线程越到await返回,执行主线程任务

子线程执行 其实是封装成委托,在task之后成为回调(编译器功能 状态机实现)

这个回调的线程是不确定的:可能是主线程,可能是子线程 以可能是其他的线程

进一步了解多线程

t.Wait();//主线程等待Task完成 或者t.Result;