Este artigo é conexão reproduzida: https://www.cnblogs.com/jesse2013/p/async-and-await.htm
assíncrona e esperar aparecer depois C # 5.0, programação paralela para trazer um monte de conveniência, especialmente quando a ação no MVC também tornar-se assíncrono, iniciar um gostinho do que é assíncrona. Mas também nos dá alguns perigos ocultos programação plantadas, às vezes podemos ter alguns não sabem como produzir o Bug, especialmente se o caso não entende mesmo o segmento, mas não sabem como lidar com eles. Bem, hoje vamos dar uma boa olhada os dois irmãos e seu tio (Task) avô (Thread) e que no final o que é a diferença, este artigo será sobre Tópico a Task .NET 4.5 e depois para o assíncrono e aguardar, três para programação paralela de uma maneira geral de introdução incluem: linha aberta, a rosca retorna um resultado, a suspensão de rosca, manuseamento excepção rosca.
indexação de conteúdo
- Criar um fio
- segmento de pool
- parâmetros
- O valor de retorno
- compartilhamento de dados
- thread-safe
- bloqueio
- Semáforo
- Manipulação de exceção
- Um pequeno exemplo de assíncrono compreensão e aguardam
- protótipo aguardam
criar
static
void
Main(){
new
Thread(Go).Start();
// .NET 1.0开始就有的
Task.Factory.StartNew(Go);
// .NET 4.0 引入了 TPL
Task.Run(
new
Action(Go));
// .NET 4.5 新增了一个Run的方法
}
public
static
void
Go(){
Console.WriteLine(
"我是另一个线程"
);
}
Deve-se notar que, depois de criar uma instância de Thread, você precisa chamar manualmente o método Start para iniciá-lo. Mas para Tarefa que, StartNew and Run, ao mesmo tempo, ambos vão criar um novo segmento, e vai começar imediatamente a ele.
segmento de pool
criação de threads é mais ocupada um recursos coisa, .NET fornece um pool de threads para nós para nos ajudar a criar e gerir threads. Tarefa é o padrão vai usar diretamente o pool de threads, mas thread não. Se não usar a tarefa, quer usar o pool de threads, você pode usar a classe ThreadPool.
tatic
void
Main() {
Console.WriteLine(
"我是主线程:Thread Id {0}"
, Thread.CurrentThread.ManagedThreadId);
ThreadPool.QueueUserWorkItem(Go);
Console.ReadLine();
}
public
static
void
Go(
object
data) {
Console.WriteLine(
"我是另一个线程:Thread Id {0}"
,Thread.CurrentThread.ManagedThreadId);
}
parâmetros de entrada
static
void
Main() {
new
Thread(Go).Start(
"arg1"
);
// 没有匿名委托之前,我们只能这样传入一个object的参数
new
Thread(
delegate
(){
// 有了匿名委托之后...
GoGoGo(
"arg1"
,
"arg2"
,
"arg3"
);
});
new
Thread(() => {
// 当然,还有 Lambada
GoGoGo(
"arg1"
,
"arg2"
,
"arg3"
);
}).Start();
Task.Run(() =>{
// Task能这么灵活,也是因为有了Lambda呀。
GoGoGo(
"arg1"
,
"arg2"
,
"arg3"
);
});
}
public
static
void
Go(
object
name){
// TODO
}
public
static
void
GoGoGo(
string
arg1,
string
arg2,
string
arg3){
// TODO
}
O valor de retorno
Thead não retornar o valor, mas como tarefa mais avançada do curso, compõem esse recurso.
static
void
Main() {
// GetDayOfThisWeek 运行在另外一个线程中
var
dayName = Task.Run<
string
>(() => {
return
GetDayOfThisWeek(); });
Console.WriteLine(
"今天是:{0}"
,dayName.Result);
}
compartilhamento de dados
O dito acima parâmetros e valores de retorno, nós olhamos para a questão da partilha de dados entre threads.
private
static
bool
_isDone =
false
;
static
void
Main(){
new
Thread(Done).Start();
new
Thread(Done).Start();
}
static
void
Done(){
if
(!_isDone) {
_isDone =
true
;
// 第二个线程来的时候,就不会再执行了(也不是绝对的,取决于计算机的CPU数量以及当时的运行情况)
Console.WriteLine(
"Done"
);
}
}
Você pode compartilhar dados entre threads através da variável estática.
thread-safe
Nós primeiro olhar para o código acima ajustes menores, eles sabem o que é thread-safe. Nós Feito abordagem duas frases para mudar sua posição.
private
static
bool
_isDone =
false
;
static
void
Main(){
new
Thread(Done).Start();
new
Thread(Done).Start();
Console.ReadLine();
}
static
void
Done(){
if
(!_isDone) {
Console.WriteLine(
"Done"
);
// 猜猜这里面会被执行几次?
_isDone =
true
;
}
}
Isso não acontece todos os acima, mas se você tiver sorte, você vai bater o jackpot. Desde o primeiro segmento ainda não teve tempo para _isDone é definido como verdadeiro, o segundo segmento entrou, mas não é o resultado que queremos, em vários segmentos, o resultado não é o resultado que esperávamos, o que não é thread-safe .
bloqueio
Para resolver os problemas acima, vamos usar o bloqueio. O tipo de bloqueio tem bloqueio exclusivo, mutex, e leitura e escrita fechaduras, etc., estamos aqui para simplesmente mostrar-lhe o bloqueio exclusivo.
private
static
bool
_isDone =
false
;
private
static
object
_lock =
new
object
();
static
void
Main(){
new
Thread(Done).Start();
new
Thread(Done).Start();
Console.ReadLine();
}
static
void
Done(){
lock
(_lock){
if
(!_isDone){
Console.WriteLine(
"Done"
);
// 猜猜这里面会被执行几次?
_isDone =
true
;
}
}
}
Depois de adicionar outro fechamento, fechado código no mesmo tempo permitindo que apenas um acesso fio, outros segmentos serão bloqueados, e somente até que o código após o bloqueio será liberado para executar outras threads estão bloqueadas.
Semaphore Semaphore
Eu realmente não sei como traduzir esta palavra, a partir da explicação oficial, podemos compreender. Ele pode controlar o número de threads a um pedaço de código ou um recurso acessado, depois de mais do que este montante, outros segmentos devem esperar, e por isso há agora somente após o lançamento da linha, o seguinte acesso thread pode. Este tem uma função similar com a fechadura, mas não exclusiva, ele permite que um certo número de acesso threads.
static
SemaphoreSlim _sem =
new
SemaphoreSlim(3);
// 我们限制能同时访问的线程数量是3
static
void
Main(){
for
(
int
i = 1; i <= 5; i++)
new
Thread(Enter).Start(i);
Console.ReadLine();
}
static
void
Enter(
object
id){
Console.WriteLine(id +
" 开始排队..."
);
_sem.Wait();
Console.WriteLine(id +
" 开始执行!"
);
Thread.Sleep(1000 * (
int
)id);
Console.WriteLine(id +
" 执行完毕,离开!"
);
_sem.Release();
}
No início, após os três primeiros para entrar na fila imediatamente executada, mas 4 e 5, é só esperar até depois há uma saída de segmento pode ser realizada.
Manipulação de exceção
Outro segmento anormal, o segmento principal pode capturá-lo?
public
static
void
Main(){
try
{
new
Thread(Go).Start();
}
catch
(Exception ex){
// 其它线程里面的异常,我们这里面是捕获不到的。
Console.WriteLine(
"Exception!"
);
}
}
static
void
Go() {
throw
null
; }
Em seguida, atualizar da Task-lo?
public
static
void
Main(){
try
{
var
task = Task.Run(() => { Go(); });
task.Wait();
// 在调用了这句话之后,主线程才能捕获task里面的异常
// 对于有返回值的Task, 我们接收了它的返回值就不需要再调用Wait方法了
// GetName 里面的异常我们也可以捕获到
var
task2 = Task.Run(() => {
return
GetName(); });
var
name = task2.Result;
}
catch
(Exception ex){
Console.WriteLine(
"Exception!"
);
}
}
static
void
Go() {
throw
null
; }
static
string
GetName() {
throw
null
; }
Um pequeno exemplo de assíncrono compreensão e aguardam
static
void
Main(
string
[] args){
Test();
// 这个方法其实是多余的, 本来可以直接写下面的方法
// await GetName()
// 但是由于控制台的入口方法不支持async,所有我们在入口方法里面不能 用 await
Console.WriteLine(
"Current Thread Id :{0}"
, Thread.CurrentThread.ManagedThreadId);
}
static
async Task Test(){
// 方法打上async关键字,就可以用await调用同样打上async的方法
// await 后面的方法将在另外一个线程中执行
await GetName();
}
static
async Task GetName(){
// Delay 方法来自于.net 4.5
await Task.Delay(1000);
// 返回值前面加 async 之后,方法里面就可以用await了
Console.WriteLine(
"Current Thread Id :{0}"
, Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(
"In antoher thread....."
);
}
protótipo aguardam
Após o aguardam ordem de execução
Graças locus da correção, aguardam mais tarde não vai abrir um novo segmento (aguardam nunca abra um novo segmento), de modo que o diagrama acima é um pequeno problema.
await não vai abrir um novo segmento, o segmento atual será sempre ir para baixo até encontrar o método assíncrono real (por exemplo HttpClient.GetStringAsync), dentro deste método será usado para abrir uma linha Task.Run ou Task.Factory.StartNew. Se o método não é o método .NET Async fornece-nos, precisamos criar uma Task-se, vai realmente ir para criar um thread .
static
void
Main(
string
[] args)
{
Console.WriteLine(
"Main Thread Id: {0}\r\n"
, Thread.CurrentThread.ManagedThreadId);
Test();
Console.ReadLine();
}
static
async Task Test()
{
Console.WriteLine(
"Before calling GetName, Thread Id: {0}\r\n"
, Thread.CurrentThread.ManagedThreadId);
var
name = GetName();
//我们这里没有用 await,所以下面的代码可以继续执行
// 但是如果上面是 await GetName(),下面的代码就不会立即执行,输出结果就不一样了。
Console.WriteLine(
"End calling GetName.\r\n"
);
Console.WriteLine(
"Get result from GetName: {0}"
, await name);
}
static
async Task<
string
> GetName()
{
// 这里还是主线程
Console.WriteLine(
"Before calling Task.Run, current thread Id is: {0}"
, Thread.CurrentThread.ManagedThreadId);
return
await Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine(
"'GetName' Thread Id: {0}"
, Thread.CurrentThread.ManagedThreadId);
return
"Jesse"
;
});
}
Vejamos esse quadro:
- Digite o thread principal começa a execução
- método assíncrono chamada retorna uma tarefa, prestar atenção desta vez outro segmento já está em execução, que é GetName dentro da Task começou a trabalhar
- O thread principal continuam a descer
- Passo 3 e Passo 4 são realizados em simultâneo, e o segmento principal não está suspensa a espera
- Se outro segmento terminar, name.IsCompleted = true, os principais trava rosca ainda não, ter um resultado direto disso. Se houver outra discussão terminou com, name.IsCompleted = false, então pendurar a thread principal para esperar até que ele retorna os resultados.
Apenas assíncrona método antes da chamada para add a esperam?
static
void
Main(){
Test();
Console.ReadLine();
}
static
async
void
Test(){
Task<
string
> task = Task.Run(() =>{
Thread.Sleep(5000);
return
"Hello World"
;
});
string
str = await task;
//5 秒之后才会执行这里
Console.WriteLine(str);
}
A resposta é óbvia: o aguardam não é dirigida a um assíncrono método, mas retornou para nós para o método Task assíncrona , razão pela qual todos os métodos assíncronos deve ser devolvido para nós de tarefas. Então, nós também pode adicionar palavras-chave aguardam na frente da tarefa, isso na verdade eu tenho que esperar para dizer ao compilador que o valor de retorno de Task ou Task, etc. Após isso está acabado para continuar a ir para baixo.
Não palavras-chave aguardam, como confirmar a tarefa é concluída?
static
void
Main(){
var
task = Task.Run(() =>{
return
GetName();
});
task.GetAwaiter().OnCompleted(() =>{
// 2 秒之后才会执行这里
var
name = task.Result;
Console.WriteLine(
"My name is: "
+ name);
});
Console.WriteLine(
"主线程执行完毕"
);
Console.ReadLine();
}
static
string
GetName(){
Console.WriteLine(
"另外一个线程在获取名称"
);
Thread.Sleep(2000);
return
"Jesse"
;
}
Diferença Task.GetAwaiter () e aguardar a tarefa de?
- Depois de adicionar palavra-chave await, a trás código irá ser suspenso de esperar até que a tarefa é o retorno final para os valores do tempo vai continuar a descer, desta vez o thread principal em um estado suspenso.
- método GetAwaiter retorna um objeto awaitable (herdado método INotifyCompletion.OnCompleted) que acabamos de passar um delegado para ele, como a conclusão da tarefa será executada esta comissão, mas que não afeta o segmento principal , o seguinte código será executado imediatamente. É por isso que temos os resultados de que a primeira frase seria "o segmento principal está consumado"!
Tarefa Como fazer os principais trava rosca esperando?
A descrição acima é parte do lado direito sem suspender o segmento principal, e nós ainda esperamos um pouco diferente, como pendurar o segmento principal antes de obter os resultados da Task-lo?
static
void
Main(){
var
task = Task.Run(() =>{
return
GetName();
});
var
name = task.GetAwaiter().GetResult();
Console.WriteLine(
"My name is:{0}"
,name);
Console.WriteLine(
"主线程执行完毕"
);
Console.ReadLine();
}
static
string
GetName(){
Console.WriteLine(
"另外一个线程在获取名称"
);
Thread.Sleep(2000);
return
"Jesse"
;
}
Task.GetAwait () método irá nos dar um objeto awaitable retorno chamando o método GetResult do objeto irá suspender o segmento principal, é claro, nem todos os casos estão pendentes. Lembre-se a natureza de nossa tarefa dele? No início da hora de começar outra thread para executar a tarefa, quando chamamos o resultado de tempo se essa tarefa foi executada, o segmento principal não está esperando pode obter um resultado, e que o thread principal está terminado, se não houver direta Nós temos que pendurar espera.
essência await é chamando o método GetResult objeto awaitable
static
async Task Test(){
Task<
string
> task = Task.Run(() =>{
Console.WriteLine(
"另一个线程在运行!"
);
// 这句话只会被执行一次
Thread.Sleep(2000);
return
"Hello World"
;
});
// 这里主线程会挂起等待,直到task执行完毕我们拿到返回结果
var
result = task.GetAwaiter().GetResult();
// 这里不会挂起等待,因为task已经执行完了,我们可以直接拿到结果
var
result2 = await task;
Console.WriteLine(str);
}
Até agora, aguardam o mistério acabou, receber comentários. Desfrute de codificação! :)