目录
Join()
join方法可以让当前线程等待join线程执行结束后再继续,
例如:A线程中,调用B线程的join方法,那么直到B线程执行完毕,才会执行A线程
join还有一个传参timespan的方法
static void Main(string[] args)
{
Thread t = new Thread(() =>{
for(int i = 0; i < 100; i++)
{
Debug.Write(i);
}
});
//也可以用new Thread(Go);
t.Start();
t.Join();
Debug.WriteLine("main");
//将会先输出1-100 然后输出main
}
static void Go()
{
for(int i = 0; i < 100; i++)
{
Debug.Write(i);
}
}
Sleep()
强制当前线程休眠,静态方法,传参可以是毫秒也可以是timespan
Thread.Sleep(2000);//当前进程休眠2秒
Thread.Sleep(0)会立马放弃当前时间片,执行交给其他线程类似Yield
Thread.Yield()线程放弃当前时间片,会把执行交给同一CPU的其他线程
ThreadState 线程状态
枚举值如下
- Running 线程正常运行
- Unstarted 线程还未开始,请调用start方法
- WaitSleepJoin 线程因为调用Wait() Sleep() Join()方法阻塞
- Stopped 线程停止
- StopRequested 线程被要求停止
- Suspended 线程被挂起,可以使用Resume()方法唤醒
- SuspendRequested 线程被要求挂起
- Aborted 中止
- AbortRequested 线程被要求中止
- Background 和线程的IsBackground属性有关,标记线程是后台线程(区别于前台线程)
线程安全
主要关注多个线程共享数据
Local:CLR为每个线程分配自己的内存栈,是本地变量保持独立
Shared:多个线程引用同一个对象实例,那么共享实例数据。
lamda表达式和静态字段也会引起数据共享
尽可能避免使用共享数据状态
简单的处理办法是加锁lock
各种锁先不展开讲
前台线程与后台线程
默认手动创建的线程都是前台线程。
一旦所有前台线程都停止了,那么程序也停止了!
用线程的IsBackground属性来判断是不是前台线程!
应用程序无法退出的原因经常是因为还有活跃的前台线程
优先级
线程的Priority属性,是枚举值
Lowest
BelowNormal
Normal
AboveNormal
Highest
如果要提升进程的优先级,那么要使用Process.GetCurrentProcess()进程,来提升进程优先级
p.PriorityClass=ProcessPriorityClass.High;
提升优先级可能会影响其他线程
信号
有时需要让线程一直等待,直到收到信号(signaling)
简单信号类ManualResetEvent
线程中收到信号对象的WaitOne()方法的时候,线程会处于等待状态
收到信号对象的Set()方法之后,线程会继续执行
可以再调用Reset()方法,再次关闭信号
Synchronization Contexts
同步上下文System.ComponentModel空间下的抽象列
是Tread Marshaling得到泛化(数据在线程间传输)
线程池Tread Pool
减少创建线程的开销!
- 不可以设置线程池名称
- 线程池是后台线程
- 阻塞线程池可能让性能降级
Debug.WriteLine(Thread.CurrentThread.IsThreadPoolThread);//检测当前线程是否属于线程池
线程池中的整洁
避免线程池中的超额订阅,超额订阅非常影响CPU性能
CLR采取任务排队节流限制来避免超额订阅
Task
Task类解决Thread的join切换线程麻烦,传入传出数据难以获取的问题
Task是一个并发操作的相对高级的抽象(类似于封装了Thead ThreadPool)
Task.Run(委托)
Task默认使用线程池(后台线程!)
task.Wait()会让task完成执行!类似于thread的join方法
Task task=Task.Run(......);
task.Wait();
task适合短时间的任务,长时间的任务另找办法!
Task<TResult>是Task的泛型子类
使用Func<TResult>委托或者兼容的Lambda表达式来调用Task.Run就可以得到Task<TResult>
随后,可以通过Result属性来获得返回结果。
如果task还没完成操作,访问Result属性会导致进程阻塞直到task任务执行完成。
Task<int> task = Task.Run(() =>
{
Thread.Sleep(3000);
return 1;
});
int result = task.Result;
Debug.WriteLine("testTask=" + result);
Continuation
回调
task对象的GetAwaiter方法会返回一个Awaiter对象
Awaiter对象的OnCompleted方法,会告诉task执行完/故障的时候,执行这个委托。
ContinueWith对于并行编程非常有用
TaskCompletionSource
创建task的一种方式,
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
new Thread(() =>
{
Thread.Sleep(5000);
tcs.SetResult(50);
})
{
IsBackground = true
}.Start();
Task<int> t2 = tcs.Task;
Debug.WriteLine("result==========="+t2.Result);
创建task并不占用线程!直到OnCompleted才占用!
一定是泛型!!
Task.Delay(5000)相当于异步版本的Thread.Sleep
Task.Delay(5000).GetAwaiter().OnCompleted(()=>Debug.WriteLine(0)));
web服务器接口功能一般都建议使用异步编程 来完成
await
简化了continuation的过程
var result=await expression;
statement(s);
就相当于
var awaiter=expression.GetAwaiter();
awaiter.OnCompleted(()=>{
var result=awaiter.GetResult();
statement(s);
});
如果一个方法是返回Task<T>的异步方法,调用时使用await关键字才能进行异步调用。
async修饰符,只能用于方法和lambda表达式,用来让编译器把await当做关键之而不是标识符,方法可以返回void、Task、Task<T>
一般用Task代替void返回
使用async修饰的方法叫异步方法。
async和await让异步编程的写法和同步的差不多,简化了异步编程的代码
对比一下正常代码和async+await代码:方便理解
static async Task Main(string[] args)
{
await Task.Run(() =>
{
for (int x = 0; x < 100; x++)
{
Debug.WriteLine("x=" + x);
}
});
for (int y = 0; y < 100; y++)
{
Debug.WriteLine("y=" + y);
}
}
//以上代码 会先输出x=0~99 再输出y=0~99
//下面的代码 x和y会交替输出
static void Main(string[] args)
{
Task.Run(() =>
{
for (int x = 0; x < 100; x++)
{
Debug.WriteLine("x=" + x);
}
});
for (int y = 0; y < 100; y++)
{
Debug.WriteLine("y=" + y);
}
}
cancellation
使用取消标识来实现对并发进行取消。
public class CacellationToken
{
//标识是否被取消
public bool IsCancellationRequested { get; private set; }
//调用者想取消的时候,会调用Cancel方法,这样会修改IsCancellationRequested为true
public void Cancel() { IsCancellationRequested = true; }
//调用者方法会通过Throw方法引发错误
public void ThrowIfCancellationRequested()
{
if (IsCancellationRequested)
throw new OperationCanceledException();
}
}
CLR提供了CancellationToken类+CancellationSource类(负责Cancel方法)
var cancelSource=new CancellationTokenSource();
Task func=Func(cancelSource.Token);
...
cancelSource.Cancel();
CancellationTokenSource还可以传递毫秒值,指定时间后取消
CancellationToken还提供了Register方法,可以注册一个回调委托,这个委托会在取消时触发(比如执行失败,返回请求失败)
它会返回一个对象,对象在取消注册的时候可以被Dispose掉
无论是故障Task还是取消的Task,都会传回一个OperationCanceledException
进度Progress
异步方法进度控制 先不说 IProgress<T>
组合Task
WhenAny 返回最先完成的Task 先了解概念不详细研究
WhenAll 参数所有Task都完成后,会返回一个Task await Task.WhenAll(Delay1(),Delay2(),Delay3()) 先了解概念不详细研究
可以编写自定义的任务组合器