1)多线程
进程 主线程和分线程关系
一个程序就是一个进程,然而进程里面包含若干个线程,而每个进程里面都有一个(可以说必须要有一个)线程,这个线程就是主线程,然而主线程有一天发现自己的工作太多了,在规定的时间内完不成工作,这时候他就召唤了一个小弟(子线程)帮他,他给小弟分配了一些任务,当小弟做完了分配给他的任务后,他就把小弟赶走了!这就是主线程和子线程。 1)如果主线程遇到了 繁重的任务 可以开辟分线程来执行任务 从而不影响主线程的执行
2.C# 到5.0 之前一共有4种创建线程的方式:
1.Thread
含义:自己创建的独立的线程, 优先级高,需要使用者自己管理
//线程无参无返回值的委托
//创建分线程1 要执行的任务(委托)
ThreadStart childref = new ThreadStart(ProgramMothod);//委托要引入的方法名
//创建分线程实例对象
Thread childThread = new Thread(childref);
childThread.Name = "分线程1";
//执行分线程
childThread.Start();
//以下输出在主线程中执行
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
Console.WriteLine("4");
Thread.CurrentThread.Name = "主线程";
//创建线程2
ThreadStart childref = () => {
Console.WriteLine("繁重的任务2");
};
Thread thread2 = new Thread(childref);
thread2.Start();
//创建线程3
Thread thread3 = new Thread(() =>
{
Console.WriteLine("繁重的任务3");
//线程休眠 单位毫秒
Thread.Sleep(2000);
Console.WriteLine("繁重的任务33");
});
//线程有一个参数无返回值的委托
ParameterizedThreadStart parameterizedThreadStart = new ParameterizedThreadStart(ProgramMothod2);//ProgramMothod2是一个有一个参数无返回值的方法
Thread thread5 = new Thread(parameterizedThreadStart);
//委托的方法 实际参数 是.Start的重载
thread5(传入的参数);
//线程执行 有一个参数的委托2
Thread thread6 = new Thread(e => {
Console.WriteLine("繁重的任务5{0}", e);
});
thread6.Start(400);
//线程阻塞 t.Join()
Thread t = new Thread(() =>
{
for (int i = 0; i < 1000; i++) Console.Write("y");
});
Thread t1 = new Thread(() => {
// t.Join();
for (int i = 0; i < 1000; i++)
Console.Write("x");
});
t.Start();
t.Join();//主线程阻塞 分线程t也会被诸塞(因为t.start()的调用发生在join前)
t1.Start();
Console.WriteLine("Thread t has ended!");
//上面线程用到的方法
//在分线程中执行
public static void ProgramMothod()
{
Console.WriteLine("繁重的任务1");
//
Console.WriteLine(Thread.CurrentThread.Name);
}
//在主线程中执行
public static void ProgramMothod1()
{
Console.WriteLine(Thread.CurrentThread.Name);
}
//有一个object类型参数 无返回值方法
public static void ProgramMothod2(object a)
{
Console.WriteLine("繁重的任务5{0}", a);
}
2)线程抢占
1.如果两个线程同时对某个资源进行同时访问、 就可能出现 线程抢占
可以用线程锁解决
static bool done;
static readonly object locker = new object(); //线程锁对象
Main方法中:
//开启分线程执行go方法
Thread thread = new Thread(Go);
thread.Name = "分线程1";
Thread thread1 = new Thread(Go);
thread1.Name = "分线程2";
thread1.Start(); //按代码的执行顺序时先启动thread1线程,那么必须等到thread1线程执行完才能执行下面的代码
thread.Start();
//设置当前主线程的名字
Thread.CurrentThread.Name = "主线程";
//
static void Go()
{
//lock线程锁 如果某个线程进入方法内时 会锁定方法
//直到当前线程执行完毕此方法时 ,后续的线程才能进入方法执行代码
//(标识对象)
lock (locker)//那个分线程先抢到线程,其他的线程就必须等到在这个线程结束之后才能执行
{
if (!done)
{
Console.WriteLine(Thread.CurrentThread.Name);
Console.WriteLine("Done");
for (int i = 0; i < 10000; i++) {
}
done = true;
}
}
}
2.ThreadPool线程池
//一个参数 WaitCallback 委托类型
//标准写法
//ThreadPool 使用线程池 操作线程
WaitCallback waitCallback = new WaitCallback(ProgramMothod);
//在分线程中执行方法
ThreadPool.QueueUserWorkItem(waitCallback);
//简写1
WaitCallback waitCallback1 = arg => Console.WriteLine("dosomething1");
ThreadPool.QueueUserWorkItem(waitCallback1);
//简写2
ThreadPool.QueueUserWorkItem(e => {
Console.WriteLine("dosomething2");
});
// 方式二:
//QueueUserWorkItem 接收两个参数,
//第一个参数是WaitCallback 委托类型,
//第二个参数是object对象,用于传递给委托函数的参数
ThreadPool.QueueUserWorkItem(e =>
{
Thread.Sleep(2000);//线程休眠
People p = e as People;
Console.WriteLine(p.Age);
}, new People() { Age = 10 });
1)ThreadPool 如何阻塞主线程
ManualResetEvent mreset = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(e =>
{
Thread.Sleep(2000);
Console.WriteLine("dosomething3");
mreset.Set();//开启主线程的mreset.waitOOne()
});
//阻塞主线程 等待分线程完成后 执行mreset.Set()后执行后续代码
mreset.WaitOne();
Console.WriteLine("阻塞主线程");
3、Task
Thread每次都会创建新线程,线程的创建和销毁是一个开销比较大的操作,Task每次执行将不会立即创建一个新线程,而是到CLR线程池查看是 否有空闲的线程,有的话就取一个线程处理这个请求,处理完请求后再把线程放回线程池,这个线程也不会立即撤销,而是设置为空闲状态,可供线程池再次调度, 从而减少开销。
开启Task分线程的三种方式
//第一种 var task1 = new Task(() => { //TODO you code }); task1.Start();//必须手动开启Start //第二种 //第二种创建方式,工厂创建,直接执行 且绑定的都是无参无返回值的委托对象 var task2 = Task.Factory.StartNew(() => { }); //第三种 Task.Run(() =>{ }); //有返回值的写法 Task<int> task = Task.Run(() => { return h(22); } );
Task的任务控制
Task比threadPool优点就是任务控制,很好的控制task的执行顺序,让多个task有序的执行
Task.Wait task1.Wait();就是等待任务执行(task1)完成,task1的状态变为Completed。 Task.WaitAll 待所有的任务都执行完成: Task.WaitAny 等待任何一个任务完成就继续向下执行 Task.ContinueWith 第一个Task完成后自动启动下一个Task,实现Task的延续 CancellationTokenSource 通过cancellation的tokens来取消一个Task。
//如果线程中的结果 需要再后续使用 使用线程延续 //线程延续 Task<int> task4 = new Task<int>(() => { return 1111; }); task4.Start(); task4.ContinueWith(tas => { Console.WriteLine("task finished!"); //获取task4 完成后的返回值结果 Console.WriteLine(tas.Result); }); //例子2:一个Task完成后自动启动下一个Task,使用Task的延续 static void Main(string[] args) { Task task = Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("1"); }); var result = task.ContinueWith<string>(tas => { Console.WriteLine("task finished!"); Task task1 = Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("2"); }); return "This is task result!"; });
Task线程取消方法
//1.初始化线程取消类 var tokenSource = new CancellationTokenSource(); //2.获取线程取消标记 var token = tokenSource.Token; //3.开启task线程 并且绑定取消线程标记 var task = Task.Run(() => { for (var i = 0; i < 1000; i++) { Thread.Sleep(1000); //是否执行取消方法 如果取消 为true 反之为 false if (token.IsCancellationRequested) { Console.WriteLine("Abort mission success!"); return; } } }, token); //取消线程后回调方法 token.Register(() => { Console.WriteLine("Canceled"); }); Console.WriteLine("Press enter to cancel task..."); Console.ReadKey(); //取消线程方法 tokenSource.Cancel(); Console.ReadKey();
4、同步异步
异步:表示执行某项操作之后不等待操作结束,但可以在操作结束后收到通知
同步:
1.net5.0推出了async/await async/await特性是与Task紧密相关的
2.async 是“异步”的简写,sync 是“同步”的简写
await 是 async wait 的简写。await 用于等待一个异步方法执行完成
//如何通过使用async/await 完成异步编程\ //1. async 必须修饰方法 被修饰的方法 表示是一个异步方法 //2.async 和await必须连用 如果不使用await 那么这个方法还是同步方法 //3.async 描述的方法 的返回值类型必须是void 或者是task 或者task<T> //4.await 描述的也是方法 但是必须是使用线程(task)的方法 //5.Async方法在执行的时候,开始是以同步的方式执行,直到遇到await关键字, //从await关键字开始,C#会另起一个线程执行await后面的代码。