菜单

【皇家编程】C#中 Thread,Task,Async/Await,IAsyncResult 的那个事儿!,

2019年5月3日 - 皇家赌场系统

2.Task

Task是.NET4.0加盟的,跟线程池ThreadPool的成效周围,用Task开启新任务时,会从线程池中调用线程,而Thread每便实例化都会创制贰个新的线程。

皇家编程 1

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

皇家编程 2

实施结果如下:

皇家编程 3

敞开新职分的诀要:Task.Run()或许Task.Factory.StartNew(),开启的是后台线程

要在主线程中等待后台线程实践落成,能够运用Wait方法(会以共同的办法来进行)。不用Wait则会以异步的办法来实行。

比较一下Task和Thread:

皇家编程 4

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

皇家编程 5

实行结果:

皇家编程 6

能够看出来,间接用Thread会开启四个线程,用Task(用了线程池)开启了一个!

 1.2 信号量(Semaphore)

 Semaphore肩负和煦线程,能够界定对某1能源访问的线程数量

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

奉行结果如下:

皇家编程 7皇家编程 8

能够观看,刚早先只有多个线程在实施,当三个线程奉行达成并释放之后,才会有新的线程来实行措施!

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

2.Task

Task是.NET肆.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("主线程结束");

实施结果如下:

皇家编程 9

敞开新职务的方法: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);
}

施行结果:

皇家编程 10

能够看出来,直接用Thread会开启四个线程,用Task(用了线程池)开启了三个!

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

 

运行结果:

 

皇家编程 11

 

透过task.Result能够取到重临值,若取值的时候,后台线程还没实践完,则会等待其实施达成!

 

总结提一下:

 

Task职分能够经过CancellationTokenSource类来撤除,以为用得不多,用法比较简单,感兴趣的话能够搜一下!

 

聊起异步,Thread,Task,async/await,IAsyncResult 必须调节

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

 推行结果如下图,

皇家编程 12

能够见见在运维后台线程之后,主线程继续往下实行了,并不曾等到后台线程试行完之后。

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

看望运转结果:

皇家编程 13

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

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

近期把GetString方法中的那行注释加上,运营的结果是:

皇家编程 14

世家能够看到,在遇见await关键字后,未有继续施行GetStrLengthAsync方法后边的操作,也未曾当即反回到main函数中,而是进行了GetString的率先行,以此能够看清await这里并从未开启新的线程去实践GetString方法,而是以协同的方法让GetString方法实行,等到试行到GetString方法中的Task<string>.Run()的时候才由Task开启了后台线程!

那正是说await的功力是如何吗?

能够从字面上通晓,上面提到task.wait能够让主线程等待后台线程试行落成,await和wait类似,同样是伺机,等待Task<string>.Run()开端的后台线程执行实现,不相同的是await不会阻塞主线程,只会让GetStrLengthAsync方法暂停试行。

那么await是怎么产生的吗?有未有展开新线程去等待?

皇家编程 15

惟有八个线程(主线程和Task开启的线程)!至于怎么实现的(小编也不知道……>_<),大家有乐趣的话切磋下呢!

 

1.线程(Thread)

多线程的意义在于一个应用程序中,有两个实行部分能够同时施行;对于比较耗费时间的操作(比如io,数据库操作),或然等待响应(如WCF通讯)的操作,能够独自开启后台线程来实施,那样主线程就不会堵塞,能够承继往下推行;等到后台线程施行达成,再通告主线程,然后做出相应操作!

在C#中展开新线程相比较轻易

皇家编程 16

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

皇家编程 17

 实施结果如下图,

皇家编程 18

能够看看在开发银行后台线程之后,主线程继续往下举办了,并不曾等到后台线程实行完之后。

2.Task

Task是.NET四.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("主线程结束");

实施结果如下:

皇家编程 19

展开新职务的秘诀: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);
}

实践结果:

皇家编程 20

可以看出来,直接用Thread会开启五个线程,用Task(用了线程池)开启了一个!

 1.2 信号量(Semaphore)

 塞马phore担负和煦线程,能够限制对某1资源访问的线程数量

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

实行结果如下:

皇家编程 21皇家编程 22

能够看看,刚开始唯有七个线程在试行,当四个线程试行实现并释放之后,才会有新的线程来实践措施!

除外SemaphoreSlim类,仍是能够使用Semaphore类,感到越来越灵敏,感兴趣的话能够搜一下,这里就不做示范了!

陆.异步的回调

 

为了简洁(偷懒),文中全部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();

 

实行结果:

 

皇家编程 23

 

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

 

实践结果:

 

皇家编程 24

 

孔蒂nueWith()方法可以让该后台线程继续试行新的职务。

 

Task的采用仍然相比灵敏的,我们能够探究下,好了,以上就是全体内容了,篇幅和力量都轻易,希望对我们有用!

Thread,Task,Async/Await,IAsyncResult
的那几个事情!, 谈起异步,Thread,Task,async/await,IAsyncResult
那么些东西一定是绕不开的,前几日就来依次…

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图