C#5.0生产了新语法,await与async,但相信我们要么很少使用它们。关于await与async有这个文章讲解,但有没有那样一种感觉,你看完后,总觉得那东西很科学,但用的时候,总是想不起来,或许不知情该怎么用。
C#中 Thread,Task,Async/Await,IAsyncResult 的那个事儿!,
说起异步,Thread,Task,async/await,IAsyncResult
那些东西一定是绕不开的,明日就来挨家挨户聊聊他们
C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!,
说起异步,Thread,Task,async/await,IAsyncResult
这几个事物必定是绕不开的,前几日就来挨家挨户聊聊他们
1.线程(Thread)
二十多线程的意思在于3个应用程序中,有多少个实施部分能够而且履行;对于相比耗时的操作(例如io,数据库操作),大概等待响应(如WCF通讯)的操作,能够单独开启后台线程来施行,那样主线程就不会卡住,能够持续往下进行;等到后台线程执行实现,再通报主线程,然后做出相应操作!
在C#中开启新线程相比简单
static void Main(string[] args)
{
Console.WriteLine("主线程开始");
//IsBackground=true,将其设置为后台线程
Thread t = new Thread(Run) { IsBackground = true };
t.Start();
Console.WriteLine("主线程在做其他的事!");
//主线程结束,后台线程会自动结束,不管有没有执行完成
//Thread.Sleep(300);
Thread.Sleep(1500);
Console.WriteLine("主线程结束");
}
static void Run()
{
Thread.Sleep(700);
Console.WriteLine("这是后台线程调用");
}
履行结果如下图
能够看看在起步后台线程之后,主线程继续往下实施了,并从未等到后台线程执行完现在。
说起异步,Thread,Task,async/await,IAsyncResult
这一个事物必定是绕不开的,后天就来挨家挨户聊聊他们
干什么呢?笔者觉得大家的await与async的打开药格局不正确。
1.线程(Thread)
四线程的含义在于二个应用程序中,有多少个执行部分能够同时执行;对于相比较耗费时间的操作(例如io,数据库操作),大概等待响应(如WCF通讯)的操作,能够独立开启后台线程来实施,那样主线程就不会阻塞,能够继续往下执行;等到后台线程执行实现,再公告主线程,然后做出相应操作!
在C#中打开新线程相比较不难
static void Main(string[] args)
{
Console.WriteLine("主线程开始");
//IsBackground=true,将其设置为后台线程
Thread t = new Thread(Run) { IsBackground = true };
t.Start();
Console.WriteLine("主线程在做其他的事!");
//主线程结束,后台线程会自动结束,不管有没有执行完成
//Thread.Sleep(300);
Thread.Sleep(1500);
Console.WriteLine("主线程结束");
}
static void Run()
{
Thread.Sleep(700);
Console.WriteLine("这是后台线程调用");
}
执行结果如下图,
能够见到在开发银行后台线程之后,主线程继续往下实行了,并不曾等到后台线程执行完之后。
1.1 线程池
试想一下,若是有雅量的职务急需处理,例如网站后台对于HTTP请求的拍卖,这是或不是要对每贰个伸手创制3个后台线程呢?分明不合适,那会占有大批量内部存款和储蓄器,而且多次地创设的进度也会严重影响进程,那如何做吧?
线程池正是为着化解这一题材,把创制的线程存起来,形成二个线程池(里面有两个线程),当要拍卖职责时,若线程池中有闲暇线程(前3个职责执行到位后,线程不会被回收,会棉被服装置为空闲状态),则一贯调用线程池中的线程执行(例asp.net处理体制中的Application对象),使用事例:
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(m =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());
});
}
Console.Read();
运转结果:
能够看来,就算实施了拾贰回,但并不曾创制11个线程。
1.2 信号量(Semaphore)
Semaphore负责协调线程,能够界定对某一能源访问的线程数量,这里对塞马phoreSlim类的用法做三个简短的例子:
static SemaphoreSlim semLim = new SemaphoreSlim(3); //3表示最多只能有三个线程同时访问
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
new Thread(SemaphoreTest).Start();
}
Console.Read();
}
static void SemaphoreTest()
{
semLim.Wait();
Console.WriteLine("线程" + Thread.CurrentThread.ManagedThreadId.ToString() + "开始执行");
Thread.Sleep(2000);
Console.WriteLine("线程" + Thread.CurrentThread.ManagedThreadId.ToString() + "执行完毕");
semLim.Release();
}
实践结果如下:
能够看出,刚起首唯有八个线程在推行,当三个线程执行完成并释放之后,才会有新的线程来推行办法!
除去SemaphoreSlim类,还能够运用Semaphore类,感觉越来越灵敏,感兴趣的话能够搜一下,那里就不做示范了!
正确的打开药格局
1.1 线程池
试想一下,如若有大量的职务须要处理,例如网站后台对于HTTP请求的处理,那是或不是要对每二个伸手创立叁个后台线程呢?鲜明不合适,那会占有大批量内部存款和储蓄器,而且多次地创立的进程也会严重影响进度,那如何是好吧?线程池正是为着消除这一题材,把创设的线程存起来,形成三个线程池(里面有四个线程),当要拍卖任务时,若线程池中有闲暇线程(前2个任务执行到位后,线程不会被回收,会棉被服装置为空闲状态),则向来调用线程池中的线程执行(例asp.net处理体制中的Application对象),
应用事例:
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(m =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());
});
}
Console.Read();
运营结果:
能够看看,即便实施了拾1次,但并没有创制十个线程。
2.Task
Task是.NET4.0出席的,跟线程池ThreadPool的效率看似,用Task开启新职务时,会从线程池中调用线程,而Thread每一次实例化都会创制3个新的线程。
Console.WriteLine("主线程启动");
//Task.Run启动一个线程
//Task启动的是后台线程,要在主线程中等待后台线程执行完毕,可以调用Wait方法
//Task task = Task.Factory.StartNew(() => { Thread.Sleep(1500); Console.WriteLine("task启动"); });
Task task = Task.Run(() => {
Thread.Sleep(1500);
Console.WriteLine("task启动");
});
Thread.Sleep(300);
task.Wait();
Console.WriteLine("主线程结束");
推行结果如下:
打开新职责的法门:Task.Run()也许Task.Factory.StartNew(),开启的是后台线程要在主线程中等待后台线程执行实现,能够行使Wait方法(会以协同的艺术来实施)。不用Wait则会以异步的不二法门来实行。
相比较一下Task和Thread:
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
new Thread(Run1).Start();
}
for (int i = 0; i < 5; i++)
{
Task.Run(() => { Run2(); });
}
}
static void Run1()
{
Console.WriteLine("Thread Id =" + Thread.CurrentThread.ManagedThreadId);
}
static void Run2()
{
Console.WriteLine("Task调用的Thread Id =" + Thread.CurrentThread.ManagedThreadId);
}
推行结果:
能够看出来,直接用Thread会开启6个线程,用Task(用了线程池)开启了2个!
1.线程(Thread)
1.2 信号量(Semaphore)
Semaphore负责协调线程,能够界定对某一能源访问的线程数量
那里对SemaphoreSlim类的用法做2个简约的例子:
static SemaphoreSlim semLim = new SemaphoreSlim(3); //3表示最多只能有三个线程同时访问
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
new Thread(SemaphoreTest).Start();
}
Console.Read();
}
static void SemaphoreTest()
{
semLim.Wait();
Console.WriteLine("线程" + Thread.CurrentThread.ManagedThreadId.ToString() + "开始执行");
Thread.Sleep(2000);
Console.WriteLine("线程" + Thread.CurrentThread.ManagedThreadId.ToString() + "执行完毕");
semLim.Release();
}
实践结果如下:
能够看出,刚开端唯有三个线程在进行,当2个线程执行完结并释放之后,才会有新的线程来施行情势!
除去塞马phoreSlim类,还是能够利用塞马phore类,感觉更是灵敏,感兴趣的话能够搜一下,那里就不做示范了!
2.1 Task<TResult>
Task<TResult>就是有再次来到值的Task,TResult正是再次来到值类型。
Console.WriteLine("主线程开始");
//返回值类型为string
Task<string> task = Task<string>.Run(() => {
Thread.Sleep(2000);
return Thread.CurrentThread.ManagedThreadId.ToString();
});
//会等到task执行完毕才会输出;
Console.WriteLine(task.Result);
Console.WriteLine("主线程结束");
运转结果:
通过task.Result能够取到重临值,若取值的时候,后台线程还没执行完,则会等待其推行完结!
简单易行提一下:
Task任务能够通过CancellationTokenSource类来撤废,感觉用得不多,用法相比简单,感兴趣的话能够搜一下!
率先看下使用约束。
2.Task
Task是.NET4.0投入的,跟线程池ThreadPool的职能相近,用Task开启新职分时,会从线程池中调用线程,而Thread每一遍实例化都会创立二个新的线程。
Console.WriteLine("主线程启动");
//Task.Run启动一个线程
//Task启动的是后台线程,要在主线程中等待后台线程执行完毕,可以调用Wait方法
//Task task = Task.Factory.StartNew(() => { Thread.Sleep(1500); Console.WriteLine("task启动"); });
Task task = Task.Run(() => {
Thread.Sleep(1500);
Console.WriteLine("task启动");
});
Thread.Sleep(300);
task.Wait();
Console.WriteLine("主线程结束");
实践结果如下:
敞开新任务的法门:Task.Run()恐怕Task.Factory.StartNew(),开启的是后台线程
要在主线程中等待后台线程执行完成,能够利用Wait方法(会以联合的方法来施行)。不用Wait则会以异步的法子来实施。
正如一下Task和Thread:
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
new Thread(Run1).Start();
}
for (int i = 0; i < 5; i++)
{
Task.Run(() => { Run2(); });
}
}
static void Run1()
{
Console.WriteLine("Thread Id =" + Thread.CurrentThread.ManagedThreadId);
}
static void Run2()
{
Console.WriteLine("Task调用的Thread Id =" + Thread.CurrentThread.ManagedThreadId);
}
实行结果:
能够看出来,间接用Thread会开启八个线程,用Task(用了线程池)开启了二个!
3. async/await
async/await是C#5.0中推出的,先上用法:
static void Main(string[] args)
{
Console.WriteLine("-------主线程启动-------");
Task<int> task = GetStrLengthAsync();
Console.WriteLine("主线程继续执行");
Console.WriteLine("Task返回的值" + task.Result);
Console.WriteLine("-------主线程结束-------");
}
static async Task<int> GetStrLengthAsync()
{
Console.WriteLine("GetStrLengthAsync方法开始执行");
//此处返回的<string>中的字符串类型,而不是Task<string>
string str = await GetString();
Console.WriteLine("GetStrLengthAsync方法执行结束");
return str.Length;
}
static Task<string> GetString()
{
//Console.WriteLine("GetString方法开始执行")
return Task<string>.Run(() =>
{
Thread.Sleep(2000);
return "GetString的返回值";
});
}
async用来修饰方法,声明这么些点子是异步的,表明的章程的回来类型必须为:void,Task或Task<TResult>。
await必须用来修饰Task或Task<TResult>,而且只可以出现在曾经用async关键字修饰的异步方法中。平常状态下,async/await成对出现才有意义,看看运转结果:
能够看出来,main函数调用GetStrLengthAsync方法后,在await在此之前,都以联合执行的,直到遇到await关键字,main函数才回到继续执行。
那正是说是还是不是是在遇见await关键字的时候程序自动开启了3个后台线程去执行GetString方法呢?
近日把GetString方法中的那行注释加上,运转的结果是:
大家能够见到,在遇到await关键字后,没有继续执行GetStrLengthAsync方法前边的操作,也尚无即刻反回到main函数中,而是进行了GetString的首先行,以此可以判断await那里并不曾拉开新的线程去履行GetString方法,而是以协同的格局让GetString方法执行,等到执行到GetString方法中的Task<string>.Run()的时候才由Task开启了后台线程!
那么await的成效是怎么着吗?
能够从字面上精晓,上边提到task.wait能够让主线程等待后台线程执行达成,await和wait类似,同样是等待,等待Task<string>.Run()开首的后台线程执行达成,分歧的是await不会阻塞主线程,只会让GetStrLengthAsync方法暂停实施。
那么await是怎么完毕的啊?有没有打开新线程去等待?
唯有多个线程(主线程和Task开启的线程)!至于怎么做到的(笔者也不知道……>_<),我们有趣味的话研商下呢!
二十八线程的意义在于多少个应用程序中,有几个实施部分能够而且履行;对于相比较耗费时间的操作(例如io,数据库操作),只怕等待响应(如WCF通讯)的操作,能够单独开启后台线程来实施,那样主线程就不会卡住,能够继续往下举行;等到后台线程执行达成,再通报主线程,然后做出相应操作!
① 、await 只可以在标记了async的函数内选用。
2.1 Task<TResult>
Task<TResult>正是有返回值的Task,TResult就是回去值类型。
Console.WriteLine("主线程开始");
//返回值类型为string
Task<string> task = Task<string>.Run(() => {
Thread.Sleep(2000);
return Thread.CurrentThread.ManagedThreadId.ToString();
});
//会等到task执行完毕才会输出;
Console.WriteLine(task.Result);
Console.WriteLine("主线程结束");
运行结果:
因而task.Result能够取到再次回到值,若取值的时候,后台线程还没执行完,则会等待其实践实现!
简言之提一下:
Task任务能够因此CancellationTokenSource类来撤销,感觉用得不多,用法相比较不难,感兴趣的话能够搜一下!
4.IAsyncResult
IAsyncResult自.NET1.1起就有了,包括可异步操作的法门的类必要完毕它,Task类就落实了该接口
在不借助Task的意况下怎么落实异步呢?
class Program
{
static void Main(string[] args)
{
Console.WriteLine("主程序开始--------------------");
int threadId;
AsyncDemo ad = new AsyncDemo();
AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);
IAsyncResult result = caller.BeginInvoke(3000,out threadId, null, null);
Thread.Sleep(0);
Console.WriteLine("主线程线程 {0} 正在运行.",Thread.CurrentThread.ManagedThreadId)
//会阻塞线程,直到后台线程执行完毕之后,才会往下执行
result.AsyncWaitHandle.WaitOne();
Console.WriteLine("主程序在做一些事情!!!");
//获取异步执行的结果
string returnValue = caller.EndInvoke(out threadId, result);
//释放资源
result.AsyncWaitHandle.Close();
Console.WriteLine("主程序结束--------------------");
Console.Read();
}
}
public class AsyncDemo
{
//供后台线程执行的方法
public string TestMethod(int callDuration, out int threadId)
{
Console.WriteLine("测试方法开始执行.");
Thread.Sleep(callDuration);
threadId = Thread.CurrentThread.ManagedThreadId;
return String.Format("测试方法执行的时间 {0}.", callDuration.ToString());
}
}
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
关键步骤正是新民主主义革命字体的局地,运营结果:
和Task的用法差别不是十分的大!result.AsyncWaitHandle.WaitOne()就就像Task的Wait。
5.Parallel
终极说一下在循环中开启四线程的回顾方法:
亚洲必赢官网 ,
Stopwatch watch1 = new Stopwatch();
watch1.Start();
for (int i = 1; i <= 10; i++)
{
Console.Write(i + ",");
Thread.Sleep(1000);
}
watch1.Stop();
Console.WriteLine(watch1.Elapsed);
Stopwatch watch2 = new Stopwatch();
watch2.Start();
//会调用线程池中的线程
Parallel.For(1, 11, i =>
{
Console.WriteLine(i + ",线程ID:" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
});
watch2.Stop();
Console.WriteLine(watch2.Elapsed);
运作结果:
循环List<T>:
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 6, 7, 8, 9 };
Parallel.ForEach<int>(list, n =>
{
Console.WriteLine(n);
Thread.Sleep(1000);
});
执行Action[]数组里面包车型客车章程:
Action[] actions = new Action[] {
new Action(()=>{
Console.WriteLine("方法1");
}),
new Action(()=>{
Console.WriteLine("方法2");
})
};
Parallel.Invoke(actions);
await与async的正确打开药方式,的这个事儿。二 、await 等待的函数必须标记async。
3. async/await
async/await是C#5.0中推出的,先上用法:
static void Main(string[] args)
{
Console.WriteLine("-------主线程启动-------");
Task<int> task = GetStrLengthAsync();
Console.WriteLine("主线程继续执行");
Console.WriteLine("Task返回的值" + task.Result);
Console.WriteLine("-------主线程结束-------");
}
static async Task<int> GetStrLengthAsync()
{
Console.WriteLine("GetStrLengthAsync方法开始执行");
//此处返回的<string>中的字符串类型,而不是Task<string>
string str = await GetString();
Console.WriteLine("GetStrLengthAsync方法执行结束");
return str.Length;
}
static Task<string> GetString()
{
//Console.WriteLine("GetString方法开始执行")
return Task<string>.Run(() =>
{
Thread.Sleep(2000);
return "GetString的返回值";
});
}
async用来修饰方法,评释这些法子是异步的,注明的主意的回来类型必须为:void,Task或Task<TResult>。
await必须用来修饰Task或Task<TResult>,而且不得不出现在曾经用async关键字修饰的异步方法中。日常情状下,async/await成对出现才有含义,
探望运营结果:
能够看出来,main函数调用GetStrLengthAsync方法后,在await以前,都以一路施行的,直到遇见await关键字,main函数才回来继续执行。
这正是说是还是不是是在碰着await关键字的时候程序自动开启了二个后台线程去实践GetString方法吧?
当今把GetString方法中的那行注释加上,运营的结果是:
大家能够看到,在遭逢await关键字后,没有继续执行GetStrLengthAsync方法后边的操作,也并未当即反回到main函数中,而是进行了GetString的率先行,以此能够判定await那里并从未开启新的线程去实施GetString方法,而是以联合的不二法门让GetString方法执行,等到执行到GetString方法中的Task<string>.Run()的时候才由Task开启了后台线程!
那么await的机能是什么啊?
能够从字面上明白,上边提到task.wait能够让主线程等待后台线程执行达成,await和wait类似,同样是伺机,等待Task<string>.Run()开端的后台线程执行完结,差别的是await不会卡住主线程,只会让GetStrLengthAsync方法暂停实施。
那就是说await是如何做到的吧?有没有打开新线程去等待?
唯有多少个线程(主线程和Task开启的线程)!至于怎么形成的(作者也不知道……>_<),大家有趣味的话研商下呢!
在C#中打开新线程相比简单
有没有觉得那是个巡回?没错,那正是个巡回。那也正是怎么大家有个别用他们的原因。这些轮回很厌恶,那么怎么消除那几个轮回呢?
4.IAsyncResult
IAsyncResult自.NET1.1起就有了,包涵可异步操作的章程的类需求达成它,Task类就完毕了该接口
在不依靠Task的景况下怎么落到实处异步呢?
class Program
{
static void Main(string[] args)
{
Console.WriteLine("主程序开始--------------------");
int threadId;
AsyncDemo ad = new AsyncDemo();
AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);
IAsyncResult result = caller.BeginInvoke(3000,out threadId, null, null);
Thread.Sleep(0);
Console.WriteLine("主线程线程 {0} 正在运行.",Thread.CurrentThread.ManagedThreadId)
//会阻塞线程,直到后台线程执行完毕之后,才会往下执行
result.AsyncWaitHandle.WaitOne();
Console.WriteLine("主程序在做一些事情!!!");
//获取异步执行的结果
string returnValue = caller.EndInvoke(out threadId, result);
//释放资源
result.AsyncWaitHandle.Close();
Console.WriteLine("主程序结束--------------------");
Console.Read();
}
}
public class AsyncDemo
{
//供后台线程执行的方法
public string TestMethod(int callDuration, out int threadId)
{
Console.WriteLine("测试方法开始执行.");
Thread.Sleep(callDuration);
threadId = Thread.CurrentThread.ManagedThreadId;
return String.Format("测试方法执行的时间 {0}.", callDuration.ToString());
}
}
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
关键步骤正是新民主主义革命字体的片段,运转结果:
和Task的用法差距不是十分的大!result.AsyncWaitHandle.WaitOne()就类似Task的Wait。
6.异步的回调
为了简洁(偷懒),文中全部Task<TResult>的重临值都以直接用task.result获取,那样只要后台任务没有履行完毕的话,主线程会等待其进行完结。这样的话就和一起一样了,一般景观下不会如此用。不难演示一下Task回调函数的运用:
Console.WriteLine("主线程开始");
Task<string> task = Task<string>.Run(() => {
Thread.Sleep(2000);
return Thread.CurrentThread.ManagedThreadId.ToString();
});
//会等到任务执行完之后执行
task.GetAwaiter().OnCompleted(() =>
{
Console.WriteLine(task.Result);
});
Console.WriteLine("主线程结束");
Console.Read();
实践结果:
OnCompleted中的代码会在任务执行到位未来执行!
另外task.ContinueWith()也是一个重庆大学的不二法门:
Console.WriteLine("主线程开始");
Task<string> task = Task<string>.Run(() => {
Thread.Sleep(2000);
return Thread.CurrentThread.ManagedThreadId.ToString();
});
task.GetAwaiter().OnCompleted(() =>
{
Console.WriteLine(task.Result);
});
task.ContinueWith(m=>{Console.WriteLine("第一个任务结束啦!我是第二个任务");});
Console.WriteLine("主线程结束");
Console.Read();
进行结果:
孔蒂nueWith()方法能够让该后台线程继续执行新的义务。
Task的使用只怕相比灵活的,大家能够钻探下,好了,以上便是全体内容了,篇幅和能力都有数,希望对大家有用!
Thread,Task,Async/Await,IAsyncResult
的那多少个事情!, 说起异步,Thread,Task,async/await,IAsyncResult
这一个东西必定是绕不开的,今天就来依次…
【很简单,await等待的是线程,不是函数。】
5.Parallel
说到底说一下在循环中打开八线程的简要方法:
Stopwatch watch1 = new Stopwatch();
watch1.Start();
for (int i = 1; i <= 10; i++)
{
Console.Write(i + ",");
Thread.Sleep(1000);
}
watch1.Stop();
Console.WriteLine(watch1.Elapsed);
Stopwatch watch2 = new Stopwatch();
watch2.Start();
//会调用线程池中的线程
Parallel.For(1, 11, i =>
{
Console.WriteLine(i + ",线程ID:" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
});
watch2.Stop();
Console.WriteLine(watch2.Elapsed);
运转结果:
循环List<T>:
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 6, 7, 8, 9 };
Parallel.ForEach<int>(list, n =>
{
Console.WriteLine(n);
Thread.Sleep(1000);
});
执行Action[]数组里面包车型地铁法子:
Action[] actions = new Action[] {
new Action(()=>{
Console.WriteLine("方法1");
}),
new Action(()=>{
Console.WriteLine("方法2");
})
};
Parallel.Invoke(actions);
static void Main(string[] args)
{
Console.WriteLine(“主线程开首”);
//IsBackground=true,将其安装为后台线程
Thread t = new Thread(Run) { IsBackground = true };
t.Start();
Console.WriteLine(“主线程在做此外的事!”);
//主线程截至,后台线程会自动终止,不管有没有履行到位
//Thread.Sleep(300);
Thread.Sleep(1500);
Console.WriteLine(“主线程甘休”);
}
static void Run()
{
Thread.Sleep(700);
Console.WriteLine(“那是后台线程调用”);
}
不知底吧?不妨,接着看下去。
6.异步的回调
为了简洁(偷懒),文中全体Task<TResult>的重临值都以一直用task.result获取,那样一旦后台任务没有执行达成的话,主线程会等待其执行实现。那样的话就和共同一样了,一般景色下不会如此用。不难演示一下Task回调函数的选择:
Console.WriteLine("主线程开始");
Task<string> task = Task<string>.Run(() => {
Thread.Sleep(2000);
return Thread.CurrentThread.ManagedThreadId.ToString();
});
//会等到任务执行完之后执行
task.GetAwaiter().OnCompleted(() =>
{
Console.WriteLine(task.Result);
});
Console.WriteLine("主线程结束");
Console.Read();
执行结果:
OnCompleted中的代码会在职分履行到位之后执行!
除此以外task.ContinueWith()也是3个重大的艺术:
Console.WriteLine("主线程开始");
Task<string> task = Task<string>.Run(() => {
Thread.Sleep(2000);
return Thread.CurrentThread.ManagedThreadId.ToString();
});
task.GetAwaiter().OnCompleted(() =>
{
Console.WriteLine(task.Result);
});
task.ContinueWith(m=>{Console.WriteLine("第一个任务结束啦!我是第二个任务");});
Console.WriteLine("主线程结束");
Console.Read();
进行结果:
ContinueWith()方法能够让该后台线程继续执行新的义务。
Task的应用如故相比较灵敏的,我们能够商量下,好了,以上正是全体内容了,篇幅和力量都有限,希望对咱们有用!
Thread,Task,Async/Await,IAsyncResult
的这一个事情!, 说起异步,Thread,Task,async/await,IAsyncResult
那么些东西一定是绕不开的,后天就来依次…
上面从头来讲解,首先看这么一组相比较
进行理并了结果如下图
public static int NoAsyncTest()
{
return 1;
}
public static async Task<int> AsyncTest()
{
return 1;
}
async Task<int>等于int
这表示大家在健康调用那八个函数时,他们是相同的。那么用async
Task<int>来修饰int目标是何许呢?
目的是为了让这些办法这么被调用 await
AsyncTest(),但一直那样调用,并不会开启线程,那那样勤奋的梳洗是或不是就没怎么含义了呢。
能够看出在起步后台线程之后,主线程继续往下执行了,并从未等到后台线程执行完事后。
自然不是,那何时会让 await AsyncTest()有意义吗?
我们跟着往下看,修改AsyncTest如下。然后,此时再调用await
AsyncTest(),你会神奇的意识,依旧没有卵用。。。
1.1 线程池
试想一下,假若有多量的任务急需处理,例如网站后台对于HTTP请求的拍卖,那是否要对种种呼吁成立二个后台线程呢?显明不合适,那会占用大量内部存款和储蓄器,而且往往地创立的进程也会严重影响进程,那如何是好呢?
线程池便是为了缓解这一难点,把创设的线程存起来,形成三个线程池(里面有八个线程),当要处理职责时,若线程池中有空闲线程(前一个任务履行到位后,线程不会被回收,会被安装为空闲状态),则直接调用线程池中的线程执行(例asp.net处理机制中的Application对象),使用事例:
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(m =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());});
}
Console.Read();
运营结果:
能够看出,即便实施了11次,但并不曾创建10个线程。
1.2 信号量(Semaphore)
Semaphore负责协调线程,能够界定对某一能源访问的线程数量,那里对SemaphoreSlim类的用法做二个简便的例证:
static SemaphoreSlim semLim = new SemaphoreSlim(3);
//3表示最三只好有四个线程同时做客static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
new Thread(SemaphoreTest).Start();
}
Console.Read();
}
static void SemaphoreTest()
{
semLim.Wait();
Console.WriteLine(“线程” +
Thread.CurrentThread.ManagedThreadId.ToString() + “开始实行”);Thread.Sleep(2000);
Console.WriteLine(“线程” +
Thread.CurrentThread.ManagedThreadId.ToString() + “执行完成”);semLim.Release();
}
实行理并了结果如下:
能够看看,刚开始只有多个线程在履行,当三个线程执行完结并释放之后,才会有新的线程来实施办法!
除了这一个之外SemaphoreSlim类,还足以选取Semaphore类,感觉特别灵活,感兴趣的话能够搜一下,那里就不做示范了!
Excute方法平常执行,而AsyncTest内运维的线程,自个儿实施本人的。
2.Task
Task是.NET4.0加盟的,跟线程池ThreadPool的功能相近,用Task开启新义务时,会从线程池中调用线程,而Thread每一趟实例化都会成立五个新的线程。
Console.WriteLine(“主线程运维”);
//Task.Run运维3个线程
//Task运行的是后台线程,要在主线程中等待后台线程执行完成,可以调用Wait方法
//Task task = Task.Factory.StartNew(() => { Thread.Sleep(1500);
Console.WriteLine(“task启动”); });Task task = Task.Run(() => {
Thread.Sleep(1500);
Console.WriteLine(“task启动”);
});
Thread.Sleep(300);
task.Wait();
Console.WriteLine(“主线程甘休”);
履行结果如下:
翻开新职分的点子:Task.Run()或许Task.Factory.StartNew(),开启的是后台线程要在主线程中等待后台线程执行完成,能够使用Wait方法(会以协同的章程来执行)。不用Wait则会以异步的措施来施行。
正如一下Task和Thread:
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
new Thread(Run1).Start();
}
for (int i = 0; i < 5; i++)
{
Task.Run(() => { Run2(); });
}
}
static void Run1()
{
Console.WriteLine(“Thread Id =” +
Thread.CurrentThread.ManagedThreadId);}
static void Run2()
{
Console.WriteLine(“Task调用的Thread Id =” +
Thread.CurrentThread.ManagedThreadId);}
履行结果:
能够看出来,直接用Thread会开启多少个线程,用Task(用了线程池)开启了三个!
public static async void Excute()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
await AsyncTest();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
}
public static async Task<int> AsyncTest()
{
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
});
return 1;
}
2.1 Task<TResult>
Task<TResult>便是有重回值的Task,TResult便是回到值类型。
Console.WriteLine(“主线程发轫”);
//重临值类型为string
Task<string> task = Task<string>.Run(() => {
Thread.Sleep(2000);
return Thread.CurrentThread.ManagedThreadId.ToString();
});
//会等到task执行完结才会输出;
Console.WriteLine(task.Result);
Console.WriteLine(“主线程结束”);
运营结果:
通过task.Result能够取到再次来到值,若取值的时候,后台线程还没实施完,则会等待其实施达成!
简言之提一下:
Task职责能够通过CancellationTokenSource类来废除,感觉用得不多,用法相比简单,感兴趣的话能够搜一下!
3. async/await
async/await是C#5.0中出产的,先上用法:
static void Main(string[] args)
{
Console.WriteLine(“——-主线程运维——-“);
Task<int> task = GetStrLengthAsync();
Console.WriteLine(“主线程继续执行”);
Console.WriteLine(“Task重返的值” + task.Result);
Console.WriteLine(“——-主线程甘休——-“);
}
static async Task<int> GetStrLengthAsync()
{
Console.WriteLine(“GetStrLengthAsync方法初步推行”);
//此处再次回到的<string>中的字符串类型,而不是Task<string>
string str = await GetString();
Console.WriteLine(“GetStrLengthAsync方法执行实现”);
return str.Length;
}
static Task<string> GetString()
{
//Console.WriteLine(“GetString方法初叶施行”)
return Task<string>.Run(() =>
{
Thread.Sleep(2000);
return “GetString的重临值”;
});
}
async用来修饰方法,评释这几个格局是异步的,表明的方法的回来类型必须为:void,Task或Task<TResult>。
await必须用来修饰Task或Task<TResult>,而且只可以出现在早就用async关键字修饰的异步方法中。平时景况下,async/await成对出现才有含义,看看运营结果:
能够看出来,main函数调用GetStrLengthAsync方法后,在await此前,都以同步施行的,直到境遇await关键字,main函数才重回继续执行。
那便是说是不是是在碰着await关键字的时候程序自动开启了二个后台线程去执行GetString方法吧?
今昔把GetString方法中的那行注释加上,运转的结果是:
世家能够见到,在遭遇await关键字后,没有继续执行GetStrLengthAsync方法前面包车型大巴操作,也远非马上反回到main函数中,而是实行了GetString的首先行,以此能够看清await那里并不曾拉开新的线程去执行GetString方法,而是以协同的主意让GetString方法执行,等到执行到GetString方法中的Task<string>.Run()的时候才由Task开启了后台线程!
那么await的效益是怎么样吗?
能够从字面上精晓,上边提到task.wait可以让主线程等待后台线程执行完成,await和wait类似,同样是伺机,等待Task<string>.Run()早先的后台线程执行实现,差别的是await不会卡住主线程,只会让GetStrLengthAsync方法暂停实施。
这正是说await是怎么形成的呢?有没有打开新线程去等待?
唯有多少个线程(主线程和Task开启的线程)!至于怎么完结的(笔者也不知道……>_<),大家有趣味的话商量下吧!
别着急,大家稍作调整,在线程前边增添.GetAwaiter().GetResult()。那句话是为什么用的吧?是用来博取线程再次来到值的。
4.IAsyncResult
IAsyncResult自.NET1.1起就有了,包括可异步操作的不二法门的类供给贯彻它,Task类就达成了该接口
在不借助于Task的图景下怎么落到实处异步呢?
class Program
{
static void Main(string[] args)
{
Console.WriteLine(“主程序发轫——————–“);
int threadId;
AsyncDemo ad = new AsyncDemo();
AsyncMethodCaller caller = new
AsyncMethodCaller(ad.TestMethod);
IAsyncResult result = caller.BeginInvoke(3000,out threadId,
null, null);Thread.Sleep(0);
Console.WriteLine(“主线程线程 {0}
正在运转.”,Thread.CurrentThread.ManagedThreadId)//会阻塞线程,直到后台线程执行完结之后,才会往下举办
result.AsyncWaitHandle.WaitOne();
Console.WriteLine(“主程序在做一些作业!!!”);
//获取异步执行的结果
string returnValue = caller.EndInvoke(out threadId, result);
//释放能源
result.AsyncWaitHandle.Close();
Console.WriteLine(“主程序截至——————–“);
Console.Read();
}
}
public class AsyncDemo
{
//供后台线程执行的点子
public string TestMethod(int callDuration, out int threadId)
{
Console.WriteLine(“测试方法开端执行.”);
Thread.Sleep(callDuration);
threadId = Thread.CurrentThread.ManagedThreadId;
return String.Format(“测试方法执行的岁月 {0}.”,
callDuration.ToString());}
}
public delegate string AsyncMethodCaller(int callDuration, out int
threadId);
关键步骤正是新民主主义革命字体的一些,运转结果:
和Task的用法差距不是十分大!result.AsyncWaitHandle.WaitOne()就接近Task的Wait。
5.Parallel
最后说一下在循环中打开四线程的回顾方法:
Stopwatch watch1 = new Stopwatch();
watch1.Start();
for (int i = 1; i <= 10; i++)
{
Console.Write(i + “,”);
Thread.Sleep(1000);
}
watch1.Stop();
Console.WriteLine(watch1.Elapsed);
Stopwatch watch2 = new Stopwatch();
watch2.Start();
//会调用线程池中的线程
Parallel.For(1, 11, i =>
{
Console.WriteLine(i + “,线程ID:” +
Thread.CurrentThread.ManagedThreadId);Thread.Sleep(1000);
});
watch2.Stop();
Console.WriteLine(watch2.Elapsed);
运作结果:
循环List<T>:
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 6, 7,
8, 9 };Parallel.ForEach<int>(list, n =>
{
Console.WriteLine(n);
Thread.Sleep(1000);
});
执行Action[]数组里面包车型客车法门:
Action[] actions = new Action[] {
new Action(()=>{
Console.WriteLine(“方法1”);
}),
new Action(()=>{
Console.WriteLine(“方法2”);
})
};
Parallel.Invoke(actions);
那一个逻辑是这么的,假诺想要获取线程重临结果,就自然要等待线程甘休。
运作一下,大家将看下边包车型客车结果。
6.异步的回调
为了简洁(偷懒),文中全体Task<TResult>的再次回到值都以平素用task.result获取,那样只要后台职分没有执行完成的话,主线程会等待其执行实现。那样的话就和共同一样了,一般景观下不会如此用。不难演示一下Task回调函数的利用:
Console.WriteLine(“主线程起首”);
Task<string> task = Task<string>.Run(() => {
Thread.Sleep(2000);
return Thread.CurrentThread.ManagedThreadId.ToString();
});
//会等到职务执行完之后执行
task.GetAwaiter().OnCompleted(() =>
{
Console.WriteLine(task.Result);
});
Console.WriteLine(“主线程结束”);
Console.Read();
进行结果:
OnCompleted中的代码会在职分履行到位以后执行!
除此以外task.孔蒂nueWith()也是三个重中之重的形式:
Console.WriteLine(“主线程伊始”);
Task<string> task = Task<string>.Run(() => {
Thread.Sleep(2000);
return Thread.CurrentThread.ManagedThreadId.ToString();
});
task.GetAwaiter().OnCompleted(() =>
{
Console.WriteLine(task.Result);
});
task.ContinueWith(m=>{Console.WriteLine(“第三个任务实现啦!小编是第①个任务”);});
Console.WriteLine(“主线程甘休”);
Console.Read();
施行结果:
ContinueWith()方法能够让该后台线程继续执行新的任务。
Task的运用照旧相比较灵活的,我们能够钻探下,好了,以上正是全体内容了,篇幅和力量都简单,希望对大家有用!
public static async Task<int> AsyncTest()
{
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
}).GetAwaiter().GetResult();
return 1;
}
不过,好像await
AsyncTest();照旧没启功能。没错,事实正是,他当真不会起成效。。。
那么怎么才能让她起效果吧?
先是,大家定义2个见惯司空函数,他的重临值是二个Task,然后我们赢得Task后,运维它,再用await等待这几个Task。
于是乎我们就得到这么的结果。
public static async void Excute()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
var waitTask = AsyncTestRun();
waitTask.Start();
int i = await waitTask;
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
}
public static Task<int> AsyncTestRun()
{
Task<int> t = new Task<int>(() => {
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
return 100;
});
return t;
}
如图,那样写await AsyncTest();就起效果了。
为此,照旧那句话,await等待的是线程,不是函数。
但在图里,大家发现很意外的少数,甘休Excute也是线程3,而不是线程1。约等于说,Await会对线程举办优化。
上面看下两组代码的周旋统一,让我们就更清楚的问询下Await。
首先组,使用await等待线程。
public static async void Excute()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
await SingleAwait();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
}
public static async Task SingleAwait()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
await Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
});
await Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
Thread.Sleep(1000);
});
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
return;
}
第②组,使用等待线程结果,等待线程。
public static async void Excute()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
await SingleNoAwait();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
}
public static async Task SingleNoAwait()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
}).GetAwaiter().GetResult();
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
Thread.Sleep(1000);
}).GetAwaiter().GetResult();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
return;
}
能够一目精晓的收看,第贰组,线程重新再次回到了主线程第11中学,而首先组,已经被优化到了线程4中。
结语
await是一种很便利的语法,他真正会让代码简洁一些,但他主动优化线程的功效,假如不打听就动用,大概会造成一些竟然的BUG产生。
那也是合法为啥只提供了await调用劳动的例子,因为,在先后内调用,await照旧要领会后,再使用,才安全。
C#语法——委托,架构的血流
C#语法——元组类型
C#语法——泛型的有余选取
注:此小说为原创,欢迎转发,请在小说页面明显地点给出此文链接!
若您觉得那篇文章还不易,请点击下右下角的推荐,极度谢谢!