繁体   English   中英

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

[英]Example of an Async Method that uses the main thread without blocking it.c#

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

例如,考虑一种发送一些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
}

一些Async方法只会在后台运行并占用后台线程,因为它们需要完成所有时间,而有些(IO基本上)则不会。

考虑这个简单的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);
    }
}

如您所见,没有创建新线程来创建异步方法。 我的自定义处理程序只是将鼠标按下事件包装在窗体上。

并非所有异步方法都需要专用线程。 通常,等待I / O完成的那些不一定需要线程。

TaskCompletionSource允许这样的场景

这是“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调用之后WriteAsync 在网络驱动程序完成其工作后,执行将继续执行直到ReadAsync,此时线程将再次释放,直到读取数据。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM