使用主线程而不阻塞它的异步方法示例。#

时间:2016-06-28 10:43:31

标签: c# asynchronous

在第18页的C#Cookbook中的并发中,Stephen Cleary定义了异步编程:“一种使用期货或回调来避免不必要线程的并发形式。” 但在代码中,所有异步方法都使用多线程(线程池)。 同样的请给我一个异步方法的例子,该方法使用主线程而不阻塞它。

4 个答案:

答案 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,此时线程将再次释放,直到读取数据。