多线程
定义
一个程序就是一个进程,而大多数的程序都由多个线程构成,进程是程序执行的最小单位。
多进程和异步也有一定的区别,所谓的异步就是在硬件不需要通过cpu直接和内存进行数据操作的行为,而线程是由cpu进行执行的
c#中多线程的使用
Thread的基本使用
Action<string> action = new Action<string>(o=> { 方法体(委托)});//通过Action就可以实现多线程;
public void ace() {
Action a = Func;
a.Invoke(“帅”);//执行该方法并且开启新的线程
a(“帅”);
a.BeginInvoke(null,null,null);//委托需要的参数,回调函数,状态参数
}
public static void Func(string s)
{
Console.WriteLine("s");
}
Invoke vs BeginInvoke
再循环执行多线程的时候 invoke实际上还是同步的过程
但是Begininvoke是异步的状态
a.BeginInvoke(null,null,null);//委托需要的参数,回调函数,状态参数
状态参数可以再 线程对象的AsyncState中取到
判断 BeginInvoke 线程是否执行完成
代码重点:x.IsCompleted, x.AsyncWaitHandle.WaitOne();
方法一 x.IsCompleted:
Action<string> a = Func;
a.Invoke("帅");//执行该方法并且开启新的线程
a("帅");
IAsyncResult x = a.BeginInvoke(null,null,null);//委托需要的参数,回调函数,状态参数
if(!x.IsCompleted) {
Console.WriteLine("!x.IsCompleted 返回的是异步线程的完成状态如果未完成返回值就为false");
}
方法二 x.AsyncWaitHandle.WaitOne():WaitOne(-1)--> 一直等待 WaitOne(1000)--> 最多等待1000毫秒,1000毫秒之后自动执行后续语句
Action<string> a = Func;
a.Invoke("帅");//执行该方法并且开启新的线程
a("帅");
IAsyncResult x = a.BeginInvoke(null,null,null);//委托需要的参数,回调函数,状态参数
x.AsyncWaitHandle.WaitOne();//等待信号量,当a线程完成之后直接执行 x.AsyncWaitHandle.WaitOne();否面的语句
EndInvoke
end ---->存放的就是F执行之后的值 当b线程执行完成之后 end会得到值
public void ace() {
Func<string> b = F;
IAsyncResult asyncResult = b.BeginInvoke(null,null);
var end = b.EndInvoke(asyncResult);
//第二种写法 返回的值同样在end 中
IAsyncResult asyncResult = b.BeginInvoke(ar=> {
var end = b.EndInvoke(ar);
}, null);
}
public static string F()
{
Console.WriteLine("s");
return "s";
}
多线程的发展 无控制的Thread 1.1 到 ThreadPool 线程池 到Task
Task 穿插知识点 并行编程Parallel —>子线程在异步执行的时候主线程也参与计算 可以启动多个线程
Task全部都是线程池线程,在线程的创建上是安全的
启动线程
Func<string> b = F;
Task<string> T = new Task<string>(b);
T.Start();
线程数组的执行
static void Main(string[] args)
{
Console.WriteLine("线程开始");
List<Task> tasklist = new List<Task>();
tasklist.Add(
Task.Run(() => { Console.WriteLine("1开始"); Thread.Sleep(200); Console.WriteLine("1结束"); }
)) ;
tasklist.Add(
Task.Run(() => { Console.WriteLine("2开始"); Thread.Sleep(200); Console.WriteLine("2结束"); }
) );
tasklist.Add(
Task.Run(() => { Console.WriteLine("3开始"); Thread.Sleep(200); Console.WriteLine("3结束"); })
);
Task.WaitAny(tasklist.ToArray());//线程中任意一个完成就返回主进程
// Task.WaitAll(tasklist.ToArray());//全部完成之后在返回主进程
Console.WriteLine("线程结束");
Console.ReadLine();
}
Task 回调的使用 task.ContinueWith((t) => Console.WriteLine(“开始回调的执行”));
Console.WriteLine("主线程开始");
Task task = Task.Run(() => { Console.WriteLine("子线程开始"); Console.WriteLine("子线程结束"); });
task.ContinueWith((t) => Console.WriteLine("开始回调的执行"));
//List<Task> tasklist = new List<Task>();
//tasklist.Add(Task.Run(() => EatApple("张三")));
//tasklist.Add(Task.Run(() => EatApple("李四")));
//tasklist.Add(Task.Run(() => EatApple("王五")));
//TaskFactory taskfactory = new TaskFactory();
//taskfactory.ContinueWhenAll(tasklist.ToArray(), tarray =>
//{
// Console.WriteLine($"苹果全部被吃完了");
//});
Console.WriteLine("主线程结束");
Console.ReadKey();
async await
当代码执行到 await是,await后面的代码就相当于子线程的回调
1.用async来修饰一个方法,表明这个方法是异步的,声明的方法的返回类型必须为:void或Task或Task。方法内部必须含有await修饰的方法,如果方法内部没有await关键字修饰的表达式,哪怕函数被async修饰也只能算作同步方法,执行的时候也是同步执行的。
2.被await修饰的只能是Task或者Task类型,通常情况下是一个返回类型是Task/Task的方法,当然也可以修饰一个Task/Task变量,await只能出现在已经用async关键字修饰的异步方法中。
static void Main(string[] args)
{
Console.WriteLine("主线程开始");
F();
Console.WriteLine("主线程结束");
}
public static async Task F()
{
var t = Task.Run(() => {
Thread.Sleep(5000);
return "Hello I am TimeConsumingMethod";
});
string Text = await t;//当子线程结束之后 可以使用变量接收子线程的结果带到回调中使用
Console.WriteLine(Text);
}
有返回值得Task
static async Task Main(string[] args)
{
Console.WriteLine("主线程开始");
string k = await F("传入的参数");
Console.WriteLine(k.ToString());//等到子线程执行完成之后才会返回,才能执行这句话 且不会卡界面
Console.WriteLine("主线程结束");
}
public static async Task<string> F(string s)
{
var t = Task.Run(() => {
Thread.Sleep(5000);
return "Hello I am TimeConsumingMethod";
});
string Text = await t;
Console.WriteLine(Text);
return string.Format("{0}+处理之后的结果", s); ;
}
多线程运行且不占用主线程 在使用线程的过程中尽量不要线程套线程
第一种方法 不推荐使用
static void Main(string[] args)
{
Console.WriteLine("主线程开始");
List<Task> tasklist = new List<Task>();
tasklist.Add(
Task.Run(() => { Console.WriteLine("1开始"); Thread.Sleep(200); Console.WriteLine("1结束"); }
)) ;
tasklist.Add(
Task.Run(() => { Console.WriteLine("2开始"); Thread.Sleep(200); Console.WriteLine("2结束"); }
) );
tasklist.Add(
Task.Run(() => { Console.WriteLine("3开始"); Thread.Sleep(200); Console.WriteLine("3结束"); })
);
//将方法体放在新线程中,解放主线程
Task.Run(() =>
{
Console.WriteLine("线程开始");
Task.WaitAny(tasklist.ToArray());//线程中任意一个完成就返回主进程
// Task.WaitAll(tasklist.ToArray());//全部完成之后在返回主进程
Console.WriteLine("线程结束");
});
Console.WriteLine("主线程结束");
Console.ReadLine();
}
第二种方法 推荐使用
static void Main(string[] args)
{
Console.WriteLine("主线程开始");
List<Task> tasklist = new List<Task>();
tasklist.Add(
Task.Run(() => { Console.WriteLine("1开始"); Thread.Sleep(200); Console.WriteLine("1结束"); }
)) ;
tasklist.Add(
Task.Run(() => { Console.WriteLine("2开始"); Thread.Sleep(200); Console.WriteLine("2结束"); }
) );
tasklist.Add(
Task.Run(() => { Console.WriteLine("3开始"); Thread.Sleep(200); Console.WriteLine("3结束"); })
);
TaskFactory taskfactory = new TaskFactory();
//等待全部任务完成之后启动一个新的线程完成后续业务
//taskfactory.ContinueWhenAll(tasklist.ToArray(),tarray =>{
// Console.WriteLine("线程123已经执行完成了,可以进行后面的业务了");
//});
//等待任意一个任务完成之后启动一个新的线程完成后续业务
taskfactory.ContinueWhenAny(tasklist.ToArray(), tarray =>
{
Console.WriteLine("线程123已经执行完成了,可以进行后面的业务了");
});
Console.WriteLine("主线程结束");
Console.ReadLine();
}
线程锁
创建线程锁
实际上线程锁就是将异步方法变同步的方式。在同一时间只有一个访问该资源的线程在执行
原理是锁定一个内存的引用地址,所以 lock (locker)的参数不可以是值类型,也不能是null
private static object locker = new object();//创建锁
锁的原则,当两个线程锁的是同一个引用类型那么他们就不能并发,当两个线程锁的变量不是同一个引用类型的话就可以并发。
apple 为公共资源,同一时间只有一个线程可以操作该资源,直到正在使用的线程释放该资源其他资源才能操作
private static int apple = 10; //10个苹果
private static object locker = new object();
static void Main(string[] args)
{
Task t1 = new Task(() => EatApple("张三"));
Task t2 = new Task(() => EatApple("李四"));
t1.Start();
t2.Start();
Console.ReadKey();
}
private static void EatApple(string name)
{
while (true)
{
lock (locker)//加锁
{
apple -= 1;
Console.WriteLine(name + "正在吃苹果");
Thread.Sleep(3000);
Console.WriteLine(name + "吃完了,还剩" + apple + "个苹果\n");
if (apple <= 1)//变为1 不然会吃-1个苹果
break;
}
}
}
写法2:
private static int apple = 10; //10个苹果
private static object locker = new object();
static void Main(string[] args)
{
List<Task> tasklist = new List<Task>();
tasklist.Add(Task.Run(() => EatApple("张三")));
tasklist.Add(Task.Run(() => EatApple("李四")));
tasklist.Add(Task.Run(() => EatApple("王五")));
TaskFactory taskfactory = new TaskFactory();
taskfactory.ContinueWhenAll(tasklist.ToArray(), tarray =>
{
Console.WriteLine($"苹果全部被吃完了");
});
Console.ReadKey();
}
private static void EatApple(string name)
{
while (true)
{
lock (locker)//加锁
{
Console.WriteLine(name + "正在吃苹果");
apple -= 1;
Thread.Sleep(1000);
Console.WriteLine(name + "吃完了,还剩" + apple + "个苹果\n");
if (apple <= 2)//变为2 不然会吃-1个苹果
break;
}
}
}
Task实例
static void Main(string[] args)
{
1.new方式实例化一个Task,需要通过Start方法启动
Task<string> task = new Task<string>(() =>
{
Console.WriteLine("task");
return $"hello, task1的ID为{Thread.CurrentThread.ManagedThreadId}";
});
task.Start();
2.Task.Factory.StartNew(Func func)创建和启动一个Task
Task<string> task2 = Task.Factory.StartNew<string>(() =>
{
Console.WriteLine("task2");
return $"hello, task2的ID为{ Thread.CurrentThread.ManagedThreadId}";
});
3.Task.Run(Func func)将任务放在线程池队列,返回并启动一个Task
Task<string> task3 = Task.Run<string>(() =>
{
Console.WriteLine("task3");
return $"hello, task3的ID为{ Thread.CurrentThread.ManagedThreadId}";
});
// 三个线程内部同时在进行执行,但是当运行到 task.Result 时 就会等待一个线程执行完毕之后在向下执行
Console.WriteLine("执行主线程!");
Console.WriteLine(task.Result);
Console.WriteLine(task2.Result);
Console.WriteLine(task3.Result);
Console.ReadKey();
}
Task 同步执行
static void Main(string[] args)
{
Task task = new Task(() =>
{
Thread.Sleep(100);
Console.WriteLine("执行Task结束!");
});
//同步执行,task会阻塞主线程
task.RunSynchronously();//当task线程执行完成之后 主进程才会向后执行
Console.WriteLine("执行主线程结束!");
Console.ReadKey();
}