如何在 C# 中将 HttpClient PostAsync() 与线程池一起使用?

Posted

技术标签:

【中文标题】如何在 C# 中将 HttpClient PostAsync() 与线程池一起使用?【英文标题】:How to use HttpClient PostAsync() with threadpool in C#? 【发布时间】:2013-12-31 03:54:02 【问题描述】:

我正在使用以下代码将图像发布到服务器。

var image= Image.FromFile(@"C:\Image.jpg");
Task<string> upload = Upload(image);
upload.Wait();

public static async Task<string> Upload(Image image)

    var uriBuilder = new UriBuilder
    
        Host = "somewhere.net",
        Path = "/path/",
        Port = 443,
        Scheme = "https",
        Query = "process=false"
    ;

    using (var client = new HttpClient())
    
        client.DefaultRequestHeaders.Add("locale", "en_US");
        client.DefaultRequestHeaders.Add("country", "US");

        var content = ConvertToHttpContent(image);
        content.Headers.ContentType = MediaTypeHeaderValue.Parse("image/jpeg");

        using (var mpcontent = new MultipartFormDataContent("--myFakeDividerText--")
            
                content, "fakeImage", "myFakeImageName.jpg"
            
        )
        
            using (
                var message = await client.PostAsync(uriBuilder.Uri, mpcontent))
            
                var input = await message.Content.ReadAsStringAsync();
                return "nothing for now";
            
        
    

我想修改此代码以运行多个线程。我之前使用过“ThreadPool.QueueUserWorkItem”并开始修改代码以利用它。

private void UseThreadPool()

    int minWorker, minIOC;
    ThreadPool.GetMinThreads(out minWorker, out minIOC);
    ThreadPool.SetMinThreads(1, minIOC);

    int maxWorker, maxIOC;
    ThreadPool.GetMaxThreads(out maxWorker, out maxIOC);
    ThreadPool.SetMinThreads(4, maxIOC);

    var events = new List<ManualResetEvent>();

    foreach (var image in ImageCollection)
    
        var resetEvent = new ManualResetEvent(false);
        ThreadPool.QueueUserWorkItem(
            arg =>
            
                var img = Image.FromFile(image.getPath());
                Task<string> upload = Upload(img);
                upload.Wait();
                resetEvent.Set();
            );
        events.Add(resetEvent);

        if (events.Count <= 0) continue;

        foreach (ManualResetEvent e in events) e.WaitOne();
    

问题是由于调用“upload.Wait()”,一次只能执行一个线程。所以我仍然按顺序执行每个线程。我不清楚如何将 PostAsync 与线程池一起使用。

如何通过调整上面的代码使用多线程将图像发布到服务器? HttpClient PostAsync 是最好的方法吗?

【问题讨论】:

【参考方案1】:

我想修改此代码以运行多个线程。

为什么?线程池只能用于 CPU 密集型工作(当然还有 I/O 完成)。

您可以使用async 进行并发处理:

var tasks = ImageCollection.Select(image =>

  var img = Image.FromFile(image.getPath());
  return Upload(img);
);
await Task.WhenAll(tasks);

请注意,我删除了您的 Wait。您应该避免将WaitResultasync 任务一起使用;请改用await。是的,这将导致async 在您的代码中增长,您应该“一直”使用async

【讨论】:

+1。使用现有功能可以让人们编写更容易推理的代码......也消除了手动管理/调试事件/线程/互斥锁和其他难以获得正确代码的所有乐趣:) 旁注:我可能会得到路径列表首先(因为它是同步的并且不太可能失败操作)然后开始上传(可能是异步读取文件,因为它是类似于 HTTP 请求的慢速 I/O 绑定操作)。 谢谢斯蒂芬。这很有效,尽管我担心一次创建太多线程,这可能会破坏 cpu 并消耗所有内存。我可能一次处理数以万计的图像。此代码一次创建所有线程。我可以分批限制它,比如一次运行几百个。线程池自动为我处理活动线程的数量。我的担忧有效吗?有没有一种平滑的方法来限制一次处理的活动线程数? @TERACytE:我发布的代码中没有使用线程;如果这令人困惑,我有一篇 async intro 博客文章可能会有所帮助。 HTTP 请求在 .NET 中具有内置限制,因此如果要限制每台服务器的请求数,只需设置 ServicePointManager.DefaultConnectionLimit。如果您也想限制文件读取(您可能会这样做),您可以使用SemaphoreSlim 再次感谢斯蒂芬。我正在考虑回答这个问题。我也会研究一下 DefaultConnectionLimit 和 SemaphoreSlim。

以上是关于如何在 C# 中将 HttpClient PostAsync() 与线程池一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# 中将 HttpClient PostAsync() 与线程池一起使用?

C# HttpClient:如何在 POST 请求中发送查询字符串

使用 HttpClient 和 C# 在 post 请求中发送 json

C#:带有 POST 参数的 HttpClient

C# HttpClient 不发送 POST 变量

使用承载令牌在 C# 中构建 post HttpClient 请求