在第18页的C#Cookbook中的并发中,Stephen Cleary定义了异步编程:“一种使用期货或回调来避免不必要线程的并发形式。” 但在代码中,所有异步方法都使用多线程(线程池)。 同样的请给我一个异步方法的例子,该方法使用主线程而不阻塞它。
答案 0 :(得分:2)
例如,考虑一个发送一些http请求并给你一些http响应的方法。如果这种方法是同步的(例如WebRequest.GetResponse()
)那么由于网络延迟,这种方法只会等待90%的时间,因此执行此方法的线程只会睡眠并且什么都不做。
当你使用异步方法(例如HttpClient.PostAsync()
)并等待结果时,调用方法以第一个await
结束,因此调用线程可以自由处理其他工作或者可以返回到ThreadPool。收到您的http回复后,您的工作将会恢复。
继续运行的线程取决于SynchronizationContext
。因此,如果您从UI线程运行并等待Async方法,则继续将在UI线程上运行。如果您从后台线程运行并等待异步方法,则继续将在某些ThreadPool线程上运行。
async void button_click(object sender, EventArgs args)
{
_button.Enabled = false; // this is invoked on main thread
var response = await _httpClient.PostAsync(request);
// you will not block main thread and your UI will be responsive
// also you won't occupy ThreadPool thread for all the time to get the response
ProcessResponse(response); // this is invoked on main thread
}
有些异步方法只会在后台运行并占用后台线程,因为它们需要完成所有时间,有些(基本上是IO)不会。
答案 1 :(得分:1)
考虑这个简单的Windows窗体程序:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
GetPoint();
}
public async void GetPoint()
{
var point = await RetrivePointAsync();
MessageBox.Show(point.ToString());
}
public Task<Point> RetrivePointAsync()
{
return Task.Factory.FromAsync<Point>(
(callback, state) => new Handler(this, callback),
x => ((Handler)x).Point, null);
}
}
class Handler : IAsyncResult
{
AsyncCallback _calback;
public Point Point { get; set; }
public object AsyncState { get { return null; } }
public bool CompletedSynchronously { get { return false; } }
public bool IsCompleted { get; set; }
public WaitHandle AsyncWaitHandle { get { return null; } }
public Handler(Control control, AsyncCallback calback)
{
_calback = calback;
control.MouseDown += control_MouseDown;
}
void control_MouseDown(object sender, MouseEventArgs e)
{
Point = e.Location;
IsCompleted = true;
_calback(this);
}
}
如您所见,没有创建新线程来创建异步方法。我的自定义处理程序只是将鼠标按下事件包装在表单上。
答案 2 :(得分:0)
并非所有异步方法都需要专用线程。通常,等待I / O完成的人不一定需要一个线程。
TaskCompletionSource允许这种情况
答案 3 :(得分:0)
这是“CLR via C#”一书的例子:
private static async Task<String> IssueClientRequestAsync(String serverName, String message) {
using (var pipe = new NamedPipeClientStream(serverName, "PipeName", PipeDirection.InOut,
PipeOptions.Asynchronous | PipeOptions.WriteThrough)) {
pipe.Connect(); // Must Connect before setting ReadMode
pipe.ReadMode = PipeTransmissionMode.Message;
// Asynchronously send data to the server
Byte[] request = Encoding.UTF8.GetBytes(message);
await pipe.WriteAsync(request, 0, request.Length);
// Asynchronously read the server's response
Byte[] response = new Byte[1000];
Int32 bytesRead = await pipe.ReadAsync(response, 0, response.Length);
return Encoding.UTF8.GetString(response, 0, bytesRead);
} // Close the pipe
}
此处执行线程在WriteAsync
调用后立即释放。在网络驱动程序完成其工作后,执行将一直持续到ReadAsync,此时线程将再次释放,直到读取数据。