概要
由一个应用场景引发的思考:程序要处理一个密集的I/O工作,在数据到达之前,程序要挂起等待。通常此时会专门启动一个I/O线程来进行处理。线程本身比较耗费资源,启动一个线程后,如果它要处理异步的任务,在没有数据时线程处于阻塞状态,这样是对系统资源的浪费,如果I/O的线程还不止一个,那浪费的资源就更多了。
这时,可以考虑使用Task.Factory.FromAsync,而不是直接使用线程。使用Task.Factory.FromAsync可以将任务调度,线程阻塞等工作交给CLR线程池引擎,由任务调度器调度,这样对于系统的使用会更加的高效。
– 异步编程模型(Asynchronous Programming Model APM)
.Net Framework 1 已经引入了这个模式,也称Begin/End 模式。一个 以Begin为前缀 的方法加载异步执行,并返回一个System.IAsyncResult对象,这个对象表示这个异步操作的状态。Begin方法可以接受一些与异步操作相关的参数,和一个System.AsyncCallback对象。这个对象表示异步操作结束时要调用的方法。然后可以调用以End为前缀的方法,以End为前缀的方法会阻塞当前任务或线程,直到I/O线程完成。它还可以返回操作结果相关的信息。
– 基于事件的异步编程模式(Event-Based Asynchronous Pattern EAP)
.Net Framework 2 为异步操作引入了这个模式。一个以Async为后缀的方法加载异步执行,当异步执行完成或取消时,异步操作会抛出一个事件,一个委托可以与这个事件绑定,并提供处理事件的回调函数。
示例程序:读入多个文件,并将文件内容合并到一起。
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading.Tasks;
namespace Sample9_1_apm_basic
{
class Program
{
private static string[] files = { "file01.txt", "file02.txt", "file03.txt", "file04.txt", "file05.txt", "file06.txt"};
private const int BUFFER_SIZE = 0x2000;
private static Task<string> ReadAllTextAsync(string strPath)
{
FileInfo info = new FileInfo(strPath);
if (!info.Exists)
{
throw new FileNotFoundException();
}
byte[] data = new byte[info.Length];
FileStream stream = new FileStream(strPath, FileMode.Open, FileAccess.Read, FileShare.Read, BUFFER_SIZE, true);
Task<int> task = Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead, data,
0, data.Length, null, TaskCreationOptions.None);
return task.ContinueWith((t) =>
{
stream.Close();
Console.WriteLine("Task Read file " + strPath + ":" + data.Length + " bytes");
return (data.Length > 0) ? new UTF8Encoding().GetString(data) : "";
}, TaskContinuationOptions.ExecuteSynchronously);
}
static void Main(string[] args)
{
List<Task<string>>fileTasks = new List<Task<string>>(files.Length);
foreach (string fileName in files)
{
var readTask = ReadAllTextAsync(fileName);
fileTasks.Add(readTask);
}
Task.WaitAll(fileTasks.ToArray());
foreach (Task<string> fileTask in fileTasks)
{
Console.WriteLine(fileTask.Result);
}
Console.ReadLine();
}
}
}
函数接口说明
这里主要用到的接口有三个:
Task.Factory.FromAsync():
对于它来说最重要的就是Begin和End函数了,但除了Begin和End 之外它还要一大串参数。这些参数就要看stream.BeginRead了。
https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory.fromasync(v=vs.110).aspx
stream.BeginRead:
接口定义:
public virtual IAsyncResult BeginRead(
byte[] buffer, int offset, int count,
AsyncCallback callback, Object state)
与程序中的接口对照一下,FromAsync()中除了Begin和End外,其他的参数基本全是给BeginRead用的。
https://msdn.microsoft.com/en-us/library/dd321290(v=vs.110).aspx
stream.EndRead:这个简单些,主要是对于返回值的。
https://msdn.microsoft.com/en-us/library/system.io.stream.endread(v=vs.110).aspx