可以单独开启后台线程来执行,如果有比较耗时

C#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,数据库操作卡塔尔(英语:State of Qatar),大概等待响应(如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("这是后台线程调用");
}

 实行结果如下图,

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

运营结果:

图片 2

能够见见,就算进行了13回,但并从未创设十个线程。

 

在使用C#编写GUI程序的时候,假若有相比耗时的操作(如图片管理、数据压缩等),咱们平时新开一个线程把那个职业交给那一个线程管理,而不放权主线程中张开操作,以防梗塞UI刷新,变成程序假死。

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

履行结果如下:

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

实行结果如下:

图片 5

开启新职分的办法:Task.Run(卡塔尔国大概Task.Factory.StartNew(卡塔尔(英语:State of Qatar),开启的是后台线程

要在主线程中等待后台线程施行实现,能够采纳Wait方法(会以合作的不二等秘书技来实施卡塔尔(英语:State of Qatar)。不用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);
}

实行结果:

图片 6

能够看出来,间接用Thread会开启5个线程,用Task(用了线程池卡塔尔(قطر‎开启了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("主线程结束");

运营结果:

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

拜访运维结果:

图片 8

能够看出来,main函数调用GetStrLengthAsync方法后,在await在此之前,都以一齐试行的,直到遇见await关键字,main函数才回到继续施行。

那么是或不是是在蒙受await关键字的时候程序自动开启了多个后台线程去推行GetString方法吧?

今昔把GetString方法中的那行注释加上,运维的结果是:

图片 9

世家可以看见,在境遇await关键字后,未有继续推行GetStrLengthAsync方法后边的操作,也不曾应声反回到main函数中,而是进行了GetString的第黄金时代行,以此能够确定await这里并未张开新的线程去实践GetString方法,而是以合作的方法让GetString方法实行,等到实施到GetString方法中的Task<string>.Run(卡塔尔的时候才由Task开启了后台线程!

那么await的效果与利益是何等呢?

能够从字面上精通,上边提到task.wait能够让主线程等待后台线程实施完结,await和wait相通,相像是伺机,等待Task<string>.Run(卡塔尔国开头的后台线程试行实现,差异的是await不会梗塞主线程,只会让GetStrLengthAsync方法暂停实行。

那么await是怎么办到的啊?有未有展开新线程去等待?

图片 10

唯有五个线程(主线程和Task开启的线程卡塔尔(英语:State of Qatar)!至于怎么形成的(我也不知道......>_<卡塔尔(英语: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类就兑现了该接口

图片 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);

关键步骤正是革命字体的部分,运营结果:

图片 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);

运维结果:

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

进行结果:

图片 14

OnCompleted中的代码会在职务执行到位现在实践!

别的task.ContinueWith(卡塔尔(英语:State of Qatar)也是三个根本的艺术:

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

推行结果:

图片 15

ContinueWith(卡塔尔国方法能够让该后台线程继续实践新的任务。

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(卡塔尔(英语:State of Qatar),试行流转入TestMain(卡塔尔(英语:State of Qatar);

那代表我们在例行调用那五个函数时,他们是相似的。那么用async Task<int>来修饰int目标是如何吧?

  1. 打印Start
  2. 调用GetValueAsync(),推行流转入GetValueAsync(卡塔尔国,注意此处是联合调用;
  3. 执行Task.Run(卡塔尔(قطر‎,生成一个新的线程并实行,同期及时回到叁个Task对象;
  4. 出于调用Task.Run(卡塔尔(英语:State of Qatar)时,是以await作为修饰的,由此是叁个异步调用,上下文情状保存第4步中回到的Task对象,在这处产生调用流窒碍,而眼下的调用语句正是调用流堵塞点,于是发生调用流堵塞再次回到,实施流回到AysncCall(卡塔尔的GetValueAsync()处,并执行下一步

目标是为着让这几个艺术这么被调用 await AsyncTest(卡塔尔国,但一贯那样调用,并不会展开线程,那这样劳顿的梳洗是或不是就没怎么意义了呢。

第5步之后就不佳解析了,因为那时早就新建了二个线程用来试行后台线程,要是电脑速度够快,那么由于新建的线程代码中有多个Thread.Sleep;,由此线程会被堵塞,于是主线程会赶在新建的线程复苏实施在此以前打字与印刷End然后Console.ReadKey()在这里地本身若是产生的是其风流浪漫情况,然后步向上面包车型地铁步子

当然不是,那什么时候会让 await AsyncTest(卡塔尔国有意义吗?

  1. 新的线程恢复生机实施,打字与印刷0 1 2 3 4 5,线程实践完结,Task对象的IsCompleted变成true

我们随后往下看,校订AsyncTest如下。然后,这个时候再调用await AsyncTest(卡塔尔(英语:State of Qatar),你会美妙的发掘,如故未有卵用。。。

  1. 此时进行流跳转到调用流拥塞点,即从调用流堵塞点恢复生机试行流,产生了调用流窒碍异步达成跳转,于是打字与印刷Task End
  2. 程序实践流结束;

Excute方法寻常实践,而AsyncTest内运转的线程,自个儿实施本人的。

紧凑钻探以上流程,能够窥见async/await最要害的地点就是调用流堵塞点,这里的阻塞并不是拥塞的线程,而是堵塞的前后相继试行流。整个经过好似七个食客走进黄金年代间旅馆点完菜,不过厨神说要等半个钟头才办好,于是先给这几个食客开了张单子让她先去外边逛风流倜傥圈,等日子到了会通报她接下来她再拿这张票来就餐(调用流堵塞异步完毕跳转);整个进程中那个食客并未在酒家做下来等,而是又去干了别的事情了。在这里间,await就是用来内定调用流拥塞点的首要字,而async则是用来标记有些方法能够被调用流梗塞的要害字。

本文由必威发布于必威-编程,转载请注明出处:可以单独开启后台线程来执行,如果有比较耗时

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。