bob体育appC#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!,

2019-12-07 23:21栏目:编程
TAG:

C#bob体育平台,5.0推出了新语法,await与async,但相信我们依旧超级少使用它们。关于await与async有过多稿子解说,但有未有与此相类似黄金年代种以为,你看完后,总感到那东西很科学,但用的时候,总是想不起来,或然不理解该怎么用。

下文以私家对async/await的知道为底子举香港行政局地证实。

  1. 调用流窒碍:不一致于线程拥塞,调用流拥塞只对函数进程起效果,调用流拥塞表示在一遍函数调用中,实行函数代码的历程中生出的无可奈何持续今后实行,供给在函数体中的某些语句甘休之处;

C#中 Thread,Task,Async/Await,IAsyncResult 的那么些事儿!,

提起异步,Thread,Task,async/await,IAsyncResult 那么些东西自然是绕不开的,今日就来挨门逐户聊聊他们

干什么呢?笔者以为大家的await与async的张开药形式不科学。

  1. 调用流阻塞点:调用流梗塞中,实践流所停下来地点的那条语句;
  2. 调用流堵塞重回:不一样于线程窒碍,调用流产生短路的时候,调用流会马上回到,在C#中,再次回到的指标能够是Task恐怕Task<T>
  3. 调用流梗塞异步完结跳转:当调用流拥塞点处的异步操作实现后,调用流被强制跳转回调用流梗塞点处实践下二个说话的景色;
  4. async传染:指的是依照C#的规定:若有些函数F的函数体中供给选用await关键字的函数必需以async标志,进一层形成急需利用await调用F的要命函数F'也必须要以async标识的情况;
  5. Task对象的装箱与拆箱:指Task<T>和T可以相互转换的情状。
  6. 异步调用:指以await作为修饰前缀举行艺术调用的调用格局,异步调用时会发生调用流梗塞。
  7. 一路调用:指不以await作为修饰前缀进行情势调用的调用情势,同步调用时不会发出调用流拥塞。

1.线程(Thread)

八线程的意思在于四个应用程序中,有多个实行部分可以同期实践;对于比较耗费时间的操作(比方io,数据库操作卡塔尔国,或然等待响应(如WCF通讯卡塔尔(英语:State of Qatar)的操作,能够独立开启后台线程来实践,这样主线程就不会堵塞,可以持续往下实践;等到后台线程执行完成,再通报主线程,然后做出相应操作!

在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("这是后台线程调用");
}

 实践结果如下图,

bob体育app 1

能够看看在开发银行后台线程之后,主线程继续往下试行了,并从未等到后台线程施行完事后。

 正确的张开药情势

async/await用于异步操作。

1.1 线程池

试想一下,假诺有恢宏的职务急需处理,例如网址后台对于HTTP央求的管理,那是否要对每多少个伸手创立三个后台线程呢?明显不合适,这会据有大量内部存款和储蓄器,並且数十次地创立的长河也会严重影响进程,那咋办吧?线程池正是为着清除那豆蔻年华主题材料,把创造的线程存起来,产生三个线程池(里面有三个线程卡塔尔(英语:State of Qatar),当要拍卖任务时,若线程池中有闲暇线程(前一个职责实施到位后,线程不会被回笼,会被设置为空闲状态卡塔尔,则直接调用线程池中的线程推行(例asp.net管理机制中的Application对象卡塔尔国,

动用事例:

for (int i = 0; i < 10; i++)
{
    ThreadPool.QueueUserWorkItem(m =>
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());
    });
}
Console.Read();

运作结果:

bob体育app 2

能够观望,固然施行了12遍,但并从未创建11个线程。

 

在使用C#编写GUI程序的时候,倘诺有比较耗时的操作(如图片管理、数据压缩等),大家日常新开三个线程把那些专业付出那些线程管理,而不放权主线程中打开操作,避防堵塞UI刷新,产生程序假死。

 1.2 信号量(Semaphore)

 塞马phore担任谐和线程,能够界定对某一财富访谈的线程数量

bob体育app, 这里对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();
}

进行结果如下:

bob体育app 3bob体育app 4

能够看来,刚开头唯有多个线程在实施,当二个线程实践实现并释放之后,才会有新的线程来实践措施!

除却SemaphoreSlim类,还足以应用Semaphore类,以为更是灵活,感兴趣的话能够搜一下,这里就不做示范了!

第生龙活虎看下使用约束。

价值观的做法是直接使用C#的Thread类(也设有其余办法,参照他事他说加以考查那篇小说)进行操作。守旧的做法在百废待举的接受编写中可能会并发回调鬼世界的问题,因此C#时下任重先生而道远推荐使用async/await来开展异步操作。

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("主线程结束");

实行结果如下:

bob体育app 5

开启新职分的主意:Task.Run(卡塔尔国或许Task.Factory.StartNew(卡塔尔(英语:State of Qatar),开启的是后台线程

要在主线程中等待后台线程试行完结,可以接受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);
}

试行结果:

bob体育app 6

能够看出来,直接用Thread会开启5个线程,用Task(用了线程池卡塔尔(英语:State of Qatar)开启了3个!

1、await 只可以在标志了async的函数内采取。

async/await通过对章程举行修饰把C#中的方法分为同步方法和异步方法两类,异步方法命名约定以Async最终。可是急需专一的是,在调用异步方法的时候,不要一定是以异步方式来拓宽调用,独有内定了以await为修饰前缀的措施调用才是异步调用

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("主线程结束");

运转结果:

bob体育app 7

透过task.Result能够取到重回值,若取值的时候,后台线程还未有实践完,则会等待其进行完成!

归纳提一下:

Task义务能够透过CancellationTokenSource类来打消,感到用得十分的少,用法比较容易,感兴趣的话能够搜一下!

2、await 等待的函数必得标识async。

考虑以下C#程序:

 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成对现身才有含义,

拜望运维结果:

bob体育app 8

能够看出来,main函数调用GetStrLengthAsync方法后,在await早先,都以协同试行的,直到遇到await关键字,main函数才回去继续施行。

那正是说是或不是是在遇见await关键字的时候程序自动开启了叁个后台线程去实施GetString方法呢?

这几天把GetString方法中的这行注释加上,运营的结果是:

bob体育app 9

大家能够看出,在遇见await关键字后,未有继续执行GetStrLengthAsync方法前边的操作,也从没当即反回到main函数中,而是举办了GetString的第大器晚成行,以此可以看清await这里并未展开新的线程去执行GetString方法,而是以三头的方法让GetString方法试行,等到施行到GetString方法中的Task<string>.Run(卡塔尔(英语:State of Qatar)的时候才由Task开启了后台线程!

这正是说await的作用是如何吧?

能够从字面上精晓,下边提到task.wait可以让主线程等待后台线程实践完成,await和wait相符,相符是等待,等待Task<string>.Run(卡塔尔(英语:State of Qatar)开首的后台线程执行达成,分化的是await不会窒碍主线程,只会让GetStrLengthAsync方法暂停施行。

那就是说await是怎么做到的吗?有未有打开新线程去等待?

bob体育app 10

独有多少个线程(主线程和Task开启的线程卡塔尔(英语:State of Qatar)!至于怎么产生的(笔者也不知道......>_<卡塔尔(قطر‎,大家有乐趣的话研讨下啊!

有未有感到那是个巡回?没有错,那正是个循环。那也正是干吗大家不怎么用他们的原由。那些轮回很讨厌,那么怎么撤消那么些轮回呢?

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;using System.Threading.Tasks;namespace ConsoleApp1{ class Program { static void Main(string[] args) { TestMain(); } static void TestMain() { Console.Out.Write("Startn"); GetValueAsync(); Console.Out.Write; Console.ReadKey(); } static async Task GetValueAsync() { await Task.Run=> { Thread.Sleep; for(int i = 0; i < 5; ++i) { Console.Out.WriteLine(String.Format("From task : {0}", i)); } }); Console.Out.WriteLine("Task End"); } }}

4.IAsyncResult

IAsyncResult自.NET1.1起就有了,蕴含可异步操作的方式的类要求实现它,Task类就贯彻了该接口

bob体育app 11

在不相信任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);

关键步骤就是蛋青字体的黄金年代部分,运转结果:

bob体育app 12

和Task的用法差距不是十分大!result.AsyncWaitHandle.WaitOne(卡塔尔就附近Task的Wait。

【超轻便,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);

运行结果:

bob体育app 13

循环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);

不知情啊?不要紧,接着看下去。

StartEndFrom task : 0

6.异步的回调

为了简洁(偷懒卡塔尔(英语:State of Qatar),文中全部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();

奉行结果:

bob体育app 14

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();

实行结果:

bob体育app 15

ContinueWith(卡塔尔(英语:State of Qatar)方法能够让该后台线程继续实行新的天职。

Task的选取也许相比灵活的,我们能够商量下,好了,以上正是全部内容了,篇幅和才能都有限,希望对我们有用!

 

Thread,Task,Async/Await,IAsyncResult 的那一个事儿!, 提及异步,Thread,Task,async/await,IAsyncResult 那个东西一定是绕不开的,前些天就来依次...

下边从头来说解,首先看这么朝气蓬勃组相比

From task : 1From task : 2From task : 3From task : 4Task End

public static int NoAsyncTest()
{
   return 1;
}
public static async Task<int> AsyncTest()
{ 
  return 1;
}

上边来解析该程序的实践流程:

 async Task<int>等于int

  1. Main(卡塔尔(قطر‎调用TestMain(卡塔尔,实践流转入TestMain(卡塔尔(قطر‎;

这意味着大家在符合规律调用这五个函数时,他们是千篇生龙活虎律的。那么用async Task<int>来修饰int目标是何许吧?

  1. 打印Start
  2. 调用GetValueAsync(),实践流转入GetValueAsync(卡塔尔国,注意此处是生龙活虎道调用;
  3. 实施Task.Run(卡塔尔,生成多少个新的线程并进行,同期及时回到二个Task对象;
  4. 出于调用Task.Run(卡塔尔(قطر‎时,是以await作为修饰的,由此是叁个异步调用,上下文意况保存第4步中回到的Task对象,在这里爆发调用流堵塞,而眼下的调用语句正是调用流梗塞点,于是发生调用流拥塞再次回到,实践流回到AysncCall(卡塔尔(قطر‎的GetValueAsync()处,并实践下一步

指标是为了让那个办法这么被调用 await AsyncTest(卡塔尔(英语:State of Qatar),但一向那样调用,并不会展开线程,那那样劳苦的梳洗是否就没怎么含义了呢。

第5步之后就倒霉解析了,因为那时早已新建了叁个线程用来施行后台线程,倘若Computer速度够快,那么由于新建的线程代码中有一个Thread.Sleep;,因而线程会被窒碍,于是主线程会赶在新建的线程苏醒实行早前打字与印刷End然后Console.ReadKey()在此间自身要是产生的是那几个状态,然后步入上面包车型地铁步子

当然不是,那何时会让 await AsyncTest(卡塔尔(قطر‎有含义吗?

  1. 新的线程苏醒奉行,打字与印刷0 1 2 3 4 5,线程实行达成,Task对象的IsCompleted变成true

我们随后往下看,改良AsyncTest如下。然后,当时再调用await AsyncTest(卡塔尔,你会美妙的意识,依然未有卵用。。。

  1. 此时实践流跳转到调用流窒碍点,即从调用流堵塞点复苏实施流,暴发了调用流窒碍异步完毕跳转,于是打字与印刷Task End
  2. 程序实行流截止;

Excute方法不荒谬实施,而AsyncTest内运维的线程,本身实施本人的。

精研以上流程,能够开采async/await最入眼的地点便是调用流拥塞点,这里的阻塞并非堵塞的线程,而是梗塞的前后相继试行流。整个经过有如八个食客走进意气风发间饭店点完菜,可是大厨说要等半个钟头才办好,于是先给那个食客开了张单子让她先去外边逛蓬蓬勃勃圈,等日子到了会通报她接下来她再拿那张票来进食(调用流堵塞异步达成跳转);整个进程中这么些食客并不以往在商旅做下来等,而是又去干了其他事情了。在这里地,await就是用来钦点调用流拥塞点的重要字,而async则是用来标记有个别方法能够被调用流堵塞的要害字。

版权声明:本文由bob体育app发布于编程,转载请注明出处:bob体育appC#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!,