Skip to content

ThreadPool hang #2083

Closed
Closed

Description

@light-years-run

Describe the bug
I use request url in ThreadPool, min Threads set to 2,Max Threads set to 10,when i run my code ,
after ExecuteGet i got hang,nothing response happen

RestSharp version 110.2.0, Net Framwork version:4.7.1

To Reproduce
here is my code

void GetMsg(object line)
        {
            string player = (string)line;

            Console.WriteLine(player);

            string tocken = "Bearer QVQxOjMuMDozLjA6MjQwOnlkU0diS0VXdTV3ZXRYdDVwQzNvUFFhSFlTOFpGVkUwZHh0Ojc4MzY1OnFvc2Z1"

            string url = "https://service-aggregation-layer.juno.ea.com/graphql";

            url += "?variables={%22searchText%22%3A%22" + player.Replace("@", "%40") + "%22%2C%22pageNumber%22%3A1%2C%22pageSize%22%3A20}&extensions={%22persistedQuery%22%3A{%22version%22%3A1%2C%22sha256Hash%22%3A%2283da6f3045ee524f6cb62a1c23eea908c9432f15e87b30dd33b89974ff83c657%22}}&operationName=SearchPlayer";

            RestClient client = new RestClient(url);
            RestRequest request = new RestRequest("");
            request.AddHeader("Authorization", tocken);
            request.AddHeader("Sec-Fetch-Site", "same-site");
            request.AddHeader("Accept", "*/*");
            request.AddHeader("Host", "service-aggregation-layer.juno.ea.com");
            request.AddHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.15.2 Chrome/83.0.4103.122 Safari/537.36 Origin/10.6.0.00000 EAApp/12.164.0.5421");

            RestResponse response = client.ExecuteGet(request);

            if (response == null || response.Content == null || response.Content == "" || response.Content.Contains("Error"))
            {
                //signal
                countdown.Signal();
                return;
            }

            ResultInfo retInfo = JsonConvert.DeserializeObject<ResultInfo>(response.Content, serializerSettings);

            List<items> all = retInfo.data.players.items;
            if (all.Count > 0)
            {
                mutex.WaitOne();
                content += (string)line;
                string displayName = all[0].displayName;
                content += "----" + displayName;
                content += Environment.NewLine;
                mutex.ReleaseMutex();
            }
            //signal
            countdown.Signal();
        }
        private void btnStart_Click(object sender, EventArgs e)
        {

            int maxCnt = int.Parse(tbxMaxThreadCount.Text);

            ThreadPool.SetMinThreads(2, 2);
            ThreadPool.SetMaxThreads(10, 10);


            string[] allLines = File.ReadAllLines("./msg.txt");

            countdown = new CountdownEvent(allLines.Length);
            foreach (string line in allLines)
            {

                ThreadPool.QueueUserWorkItem(new WaitCallback(GetMsg), line);
            }

            // all done
            countdown.Wait();

            //write to file
            File.WriteAllText("./out.txt", content);
            MessageBox.Show("all done");
        }

Stack trace
Copy the full stack trace here if you get an exception.

Desktop (please complete the following information):

  • OS: [e.g. macOS]
  • .NET version [e.g. .NET 5]
  • Version [e.g. 107.0.4]

Additional context
Add any other context about the problem here.

Activity

  1. rassilon commented on Mar 7, 2024

    @rassilon

    This seems like a classic case of artificial bounds on thread pool max thread size ends up causing a deadlock of some sort due to async keyword usage and the synchronous methods not actually being synchronous internally (somehow)..

    i.e. something like this might be happening:

    • The file you're reading in to create messages might have, say, 100 lines.
    • The for loop to queue all of the work items to the thread pool might queue all 100 lines before GetMsg has a chance to invoke RestSharp
    • When RestSharp transitions to async keyword-related code (or invokes code that does so for it), the invoked code wants to queue additional work items to the thread pool, but due to the thread pool maximum thread count size constraint, you supplied, these work items are stuck in the thread pool queue and don't get any execution time.
    • This causes the thread pool threads all to get blocked, with some call stack waiting on some other thread pool item that will not ever execute because it's stuck in the thread pool work list and is further back in the queue.

    This is a basic async or distributed processing system issue that can occur when the interfaces to constrained systems don't have built-in support for providing back pressure to the callers. (which isn't easy to do or easy to use once accomplished)

    This also might be exacerbated by #2160, but I'm not sure there's necessarily a lot the RestSharp developers should do in this area above and beyond #2160.

  2. vocko commented on Jun 7, 2024

    @vocko

    We've just noticed the same issue on .NET 4.8 with synchronous calls.

    var response = _restClient.Get(request);
    

    The thread just hangs and keeps chewing memory until it causes the instance to crash.

    We have replaced it with the

    var response = _restClient.ExecuteRequest(request);
    

    and it works fine.

    Not sure what's the difference internally but might worth to remove synchronous method specific overloads (if finding out the issue proves too complicated).

    I suppose it has something to do with the thread synchronisation and would be great if we can use the async calls but this is a legacy app and refactoring is just not worth it.

  3. alexeyzimarev commented on Feb 19, 2026

    @alexeyzimarev
    Member

    There's no difference internally. The same Async wrapper is used in both cases. The code is there, you can check it. I don't know what it is really.

  4. deAtog commented on Mar 4, 2026

    @deAtog

    After upgrading from 106.15.0 to 113.1.0 I got this hang as well. I reviewed the code, and found the following locations fail to call .ConfigureAwait(false) when using await:

    Line 48 of AsyncHelpers.cs:

    /// <returns>Return value from the task</returns>
    public static T RunSync<T>(Func<Task<T>> task) {
    T result = default!;
    RunSync(async () => { result = await task(); });
    return result;
    }

    Line 35 of HttpResponseExtensions.cs:

    public static async Task<string> GetResponseString(this HttpResponseMessage response, byte[] bytes, Encoding clientEncoding) {
    var encodingString = response.Content.Headers.ContentType?.CharSet;
    var encoding = encodingString != null ? TryGetEncoding(encodingString) : clientEncoding;
    using var reader = new StreamReader(new MemoryStream(bytes), encoding);
    return await reader.ReadToEndAsync();
    Encoding TryGetEncoding(string es) {
    try {
    return Encoding.GetEncoding(es);
    }
    catch {

    Line 57 of RestResponse.cs:

    #if NET
    await using var stream = await httpResponse.ReadResponseStream(request.ResponseWriter, cancellationToken).ConfigureAwait(false);
    #else
    using var stream = await httpResponse.ReadResponseStream(request.ResponseWriter, cancellationToken).ConfigureAwait(false);
    #endif
    var bytes = stream == null ? null : await stream.ReadAsBytes(cancellationToken).ConfigureAwait(false);
    var content = bytes == null ? null : await httpResponse.GetResponseString(bytes, options.Encoding);
    return new(request) {
    Content = content,
    ContentEncoding = httpResponse.Content?.Headers.ContentEncoding ?? Array.Empty<string>(),
    ContentHeaders = httpResponse.Content?.Headers.GetHeaderParameters(),
    ContentLength = httpResponse.Content?.Headers.ContentLength,
    ContentType = httpResponse.Content?.Headers.ContentType?.MediaType,
    Cookies = cookieCollection,

    Line 48 of RestClient.Extensions.cs:

    public async Task<RestResponse<T>> ExecuteAsync<T>(
    RestRequest request,
    CancellationToken cancellationToken = default
    ) {
    Ensure.NotNull(request, nameof(request));
    var response = await client.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
    return await client.Serializers.Deserialize<T>(request, response, client.Options, cancellationToken);
    }

    Lines 175 and 177 of RestClient.Extensions.cs:

    #if NET7_0_OR_GREATER
    while (await reader.ReadLineAsync(cancellationToken) is { } line && !cancellationToken.IsCancellationRequested) {
    #else
    while (await reader.ReadLineAsync() is { } line && !cancellationToken.IsCancellationRequested) {
    #endif
    if (string.IsNullOrWhiteSpace(line)) continue;
    var response = new RestResponse(request) { Content = line };
    yield return serializer.Deserializer.Deserialize<T>(response)!;
    }

    I have since refactored my code to use async/await and the Async methods of RestSharp so I do not know if the above would have corrected the hang in my application.

  5. added a commit that references this issue on Mar 5, 2026
    b4dc236
  6. added a commit that references this issue on Mar 5, 2026
    c362621
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions