异步定义
异步用途 通常用于执行完成时间较长的任务,比如:打开大文件、连接远程计算机、查询数据库等操作。应用程序调用方法异步执行某个操作时,应用程序可以在异步方法执行其任务时继续执行。
异步机制 异步编程采用future或callback机制,以避免产生不必要的线程。
异步核心 启动了的操作将在一段时间后完成, 这个操作正在执行时,不会阻塞原来的线程。启动了这个操作的线程可以继续执行其他任务,当操作完成时,会通知回调函数。
异步与同步异步与同步是相对于同步而言,跟多线程不能同一而论。同步执行是执行某个任务时,需要在该任务完成后才能继续执行另一个任务。异步执行在执行某个任务时,可以在该任务完成之前执行另一个任务。异步执行最重要的体现就是不排队,不阻塞。
单线程执行
多线程执行
异步与多线程
异步可以在单个线程上实现,也可以在多个线程上实现,还可以不需要线程(一些IO操作)。
只要存在多线程就存在异步,多线程就是异步的表现。
异步应用
.NET Framework的许多方面都支持异步编程功能:
- 文件IO、流IO、套接字IO
- 网络
- 远程处理信道:HTTP、TCP和代理
- 使用ASP.NET创建的XML WebServices
- ASP.NET Web窗体
- 使用MessageQuenue类的消息队列
.Net Framework为异步操作提供了两种设计模式
- 使用IAsyncReault对象的异步操作
- 使用事件的异步操作
Task
Task本身就表示一个异步操作,默认是运行在线程池里的线程,更轻量,可以更高效的利用线程:
- 实现了控制任务执行的顺序
- 实现父子任务
- 实现任务的取消操作
- 实现了进度报告
- 实现了返回值
- 实现了随时查看任务状态
Task启动与同步
Task.RunSynchronously
//启用线程池中的线程异步执行
Task t1 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Task--1启动...");
});
//启用线程池中的线程异步执行
Task t2 = Task.Run(() =>
{
Console.WriteLine("Task--2启动...");
});
Task t3 = new Task(() =>
{
Console.WriteLine("Task--3启动...");
});
// t3.Start();//启用线程池中的线程异步执行
t3.RunSynchronously();//任务与主线程同步执行
Task.WaitAll
Task t1 = Task.Run(() =>
{
Console.WriteLine("Task--1启动...");
});
Task t2 = Task.Run(() =>
{
Console.WriteLine("Task--2启动...");
});
//调用WaitAll() ,会阻塞调用线程,等待任务执行完成 ,这时异步也没有意义了
Task.WaitAll(new Task[] {
t1, t2 });
Console.WriteLine("Task完成...");
Task等待任务结果,处理结果
Task.ContinueWith
Task t1 = Task.Run(() =>
{
Console.WriteLine("Task-1启动...");
});
Task t2 = Task.Run(() =>
{
Console.WriteLine("Task-2启动...");
});
//调用WaitAll() ,会阻塞调用线程,等待任务执行完成 ,这时异步也没有意义了
Task.WaitAll(new Task[] {
t1, t2 });
Console.WriteLine("Task完成...");
//调用ContinueWith,等待任务完成,触发下一个任务,这个任务可当作任务完成时触发的回调函数。
//为了获取结果,同时不阻塞调用线程,建议使用ContinueWith,在任务完成后,接着执行一个处理结果的任务。
t1.ContinueWith((t) =>
{
Console.WriteLine("Task-1完成...");
});
t2.ContinueWith((t) =>
{
Console.WriteLine("Task-2完成...");
});
Task.GetAwaiter().OnCompleted
Task t1 = Task.Run(() =>
{
Console.WriteLine("Task-1启动...");
});
Task t2 = Task.Run(() =>
{
Console.WriteLine("Task-2启动...");
});
//调用WaitAll() ,会阻塞调用线程,等待任务执行完成 ,这时异步也没有意义了
Task.WaitAll(new Task[] {
t1, t2 });
Console.WriteLine("Task完成...");
//调用GetAwaiter()方法,获取任务的等待者,调用OnCompleted事件,当任务完成时触发
//调用OnCompleted事件也不会阻塞线程
t1.GetAwaiter().OnCompleted(() =>
{
Console.WriteLine("Task-1完成...");
});
t2.GetAwaiter().OnCompleted(() =>
{
Console.WriteLine("Task-2完成...");
});
Task任务取消
//实例化一个取消实例
var source = new CancellationTokenSource();
var token = source.Token;
Task t1 = Task.Run(() =>
{
Thread.Sleep(2000);
//判断是否任务取消
if (token.IsCancellationRequested)
{
// 调用该方法后线程状态才会变为Canceled状态
token.ThrowIfCancellationRequested();
//Console.WriteLine("任务已取消");
}
Thread.Sleep(500);
//token传递给任务
}, token);
Thread.Sleep(1000);
Console.WriteLine(t1.Status);
Thread.Sleep(1000);
//取消该任务
source.Cancel();
Thread.Sleep(1000);
Console.WriteLine(t1.Status);
Console.ReadKey();
Task返回值
public static void Test6()
{
Task<string> t1 = Task.Run(() => Tttst("HELLOW"));
t1.Wait();//阻塞,等待结果
Console.WriteLine(t1.Result);
}
static string Tttst(string STR)
{
return STR + " FROM TEST";
}
Async await
异步方法执行流程:
主线程调用MethodAsync方法,并等待方法执行结束
异步方法开始执行,输出“异步执行”
异步方法执行到await关键字,此时MethodAsync方法挂起,等待await表达式执行完毕,同时将控制权返回给调用方主线程,主线程继续执行。
执行Task.Delay方法,同时主线程继续执行之后的方法。
Task.Delay结束,await表达式结束,MehtodAsync执行await表达式之后的语句,输出“异步执行结束”。
public static async void MethodAsync()
{
Console.WriteLine("异步执行");
await Task.Delay(1000);
Console.WriteLine("异步执行结束");
}
Task.Delay与Thread.Sleep
Thread.Sleep会阻塞线程。而Task.Delay创建一个将在设置时间后执行的任务,就相当于一个定时器,多少时间后再执行操作,不会阻塞执行线程。
public static async Task Test7Async()
{
Console.WriteLine("await执行前..." + Thread.CurrentThread.ManagedThreadId);
await Task.Run(() =>
{
Console.WriteLine("await执行..." + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
Console.WriteLine("await执行结束..." + Thread.CurrentThread.ManagedThreadId);
});
Thread.Sleep(2000);
Console.WriteLine("await之后执行..." + Thread.CurrentThread.ManagedThreadId);
}
并行库Parallel类
该类位于System.Threading.Tasks名称空间中,提供了数据和任务并行性。
Parallel.Invoke
如果多个任务并行运行,可以使用Parallel.Invoke方法。该方法允许传递一个Action委托数组。
/// <summary>
/// 并行执行多个任务
/// </summary>
public static void ParalelDemo1()
{
// 通过Paralel类对象来并行执行3个任务
Action task1 = () =>
{
for (int i = 0; i < 1000000; i++)
{
Console.WriteLine($"1111111i=" + (i * 4));
}
};
Action task2 = () =>
{
for (int i = 0; i < 1000000; i++)
{
Console.WriteLine($"222222i=" + (i * 4));
}
};
Action task3 = () =>
{
for (int i = 0; i < 1000000; i++)
{
Console.WriteLine($"33333333i=" + (i * 4));
}
};
// 并行执行
Parallel.Invoke(task1, task2, task3);
}
Parallel.For
Parallel.For()方法类似C#语法的for循环语句,多次执行一个任务。但该方法并行运行迭代,迭代的顺序没有定义。
Parallel.For()方法中,前两个参数定义了循环的开头和结束,第三个参数是一个Action委托。Parallel.For方法返回类型是ParallelLoopResult结构,它提供了循环是否结束的信息。
Parallel.For有多个重载版本和多个泛型重载版本。
/// <summary>
/// 并行For循环
/// </summary>
public static void ParallelDemo2()
{
ParallelLoopResult plr = Parallel.For(0, 100, i =>
{
Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
});
if (plr.IsCompleted)
{
Console.WriteLine("completed!");
}
}
停止Parallel.For
ParallelLoopResult plr = Parallel.For(0, 100, (int i, ParallelLoopState pls) =>
{
Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
if (i > 5) pls.Break();
});
Console.WriteLine("is completed:{0}", plr.IsCompleted);
Console.WriteLine("最低停止索引:{0}", plr.LowestBreakIteration);
Parallel.ForEach
Parallel.ForEach方法遍历实现了IEnumerable的集合,类似于foreach,但以异步方式遍历。没有确定遍历顺序。
string[] data = {
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve"
};
ParallelLoopResult plr = Parallel.ForEach<string>(data, s =>
{
Console.WriteLine(s);
});
if (plr.IsCompleted)
Console.WriteLine("completed!");
Task 异步实例
同步与异步区别
同步:
public static void LongProcess()
{
Console.WriteLine("LongProcess===>批量读取2000个点位数据");
//模拟读取数据需要时间5秒
Thread.Sleep(5000);//会阻塞线程
Console.WriteLine("LongProcess===>批量读取数据结束。。。");
}
public static void ShortProcess()
{
Console.WriteLine("ShortProcess===>批量读取200个点位数据");
//模拟读取数据需要时间5秒
Thread.Sleep(5000);
Console.WriteLine("ShortProcess===>批量读取数据结束。。。");
}
异步:
public static async void LongAsyncProcess()
{
Console.WriteLine("LongProcess===>Async批量读取2000个点位数据");
//模拟读取数据需要时间5秒
await Task.Delay(5000);
Console.WriteLine("LongProcess===>Async批量读取数据结束。。。");
}
public static void ShortProcess()
{
Console.WriteLine("ShortProcess===>批量读取200个点位数据");
//模拟读取数据需要时间5秒
Thread.Sleep(5000);
Console.WriteLine("ShortProcess===>批量读取数据结束。。。");
}
异步方法
不带返回值的异步方法
public async static void TestTask()
{
await Task.Run(() => {
});
}
public async static Task TestTask1()
{
await Task.Run(() => {
});
}
带返回值的异步方法与调用方法
/// <summary>
/// 异步方法调用
/// </summary>
public static async void TestASYNC()
{
//方法一 异步执行
Task<int> result = LongProcesss();
var var2 = await result;
方法二 阻塞当前线程
var var2 = await LongProcesss1();
Console.WriteLine($"Result:{
var2}");
}
/// <summary>
/// 带返回值的异步方法
/// </summary>
/// <returns></returns>
public async static Task<int> LongProcesss()
{
Console.WriteLine("异步线程开始");
await Task.Delay(5000);
return 10;
}