TERACytE
TERACytE

Reputation: 7863

How to use HttpClient PostAsync() with threadpool in C#?

I'm using the following code to post an image to a server.

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";
            }
        }
    }
}

I'd like to modify this code to run multiple threads. I've used "ThreadPool.QueueUserWorkItem" before and started to modify the code to leverage it.

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();
    }
}

The problem is that only one thread executes at a time due to the call to "upload.Wait()". So I'm still executing each thread in sequence. It's not clear to me how I can use PostAsync with a thread-pool.

How can I post images to a server using multiple threads by tweaking the code above? Is HttpClient PostAsync the best way to do this?

Upvotes: 0

Views: 2784

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 457217

I'd like to modify this code to run multiple threads.

Why? The thread pool should only be used for CPU-bound work (and I/O completions, of course).

You can do concurrency just fine with async:

var tasks = ImageCollection.Select(image =>
{
  var img = Image.FromFile(image.getPath());
  return Upload(img);
});
await Task.WhenAll(tasks);

Note that I removed your Wait. You should avoid using Wait or Result with async tasks; use await instead. Yes, this will cause async to grow through you code, and you should use async "all the way".

Upvotes: 6

Related Questions