You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
RestClient.Execute() is extremely slow compared to RestClient.ExecuteAsync().Result #2375
Describe the bug RestClient.Execute() is extremely slow compared to RestClient.ExecuteAsync().Result, when called in a parallel setting, even though both are synchronous.
To Reproduce
private class CustomInterceptor : Interceptor
{
public override ValueTask BeforeDeserialization ( RestResponse response, CancellationToken cancellationToken )
{
response.ContentType = ContentTypes.application_json;
return ValueTask.CompletedTask;
}
}
static void Test02 ()
{
//
const int total = 100;
int width = ( int ) Math.Ceiling( Math.Log10( total ) );
( int, TimeSpan )[] elapsed = new ( int, TimeSpan )[total];
//
RestResponse Execute ( RestClient c, string resource, Method method, Action<RestRequest> callback )
{
//
RestRequest i;
RestResponse o;
//
if ( c == null )
throw new ArgumentNullException( nameof( c ) );
i = new RestRequest( resource, method );
callback?.Invoke( i );
i.Interceptors = [];
i.Interceptors.Add( new CustomInterceptor() );
o = c.Execute( i );
return o;
}
void Test ( int i )
{
//
Stopwatch watch;
//
static void Add ( RestRequest t )
{
t.AddHeader( HTTPHeaders.Content_Type, ContentTypes.application_json );
}
watch = Stopwatch.StartNew();
using ( var c = new RestClient( some server ) )
Execute( c, "", Method.Post, Add );
watch.Stop();
elapsed[i] = ( i, watch.Elapsed );
}
Parallel.For( 0, total, Test );
////
Array.Sort( elapsed, ( x, y ) => y.Item2.CompareTo( x.Item2 ) );
for ( int i = 0; i < total; i++ )
Console.WriteLine( $@"{elapsed[i].Item1.ToString().PadLeft( width )} => {elapsed[i].Item2:mm\:ss\.fff}" );
}
If you use the above code, which uses c.Execute( i ), to communicate to a server, there will be very long delays (up to roughly 10 s). If you replace c.Execute( i ) with c.ExecuteAsync( i ).Result, the time is reduced to just roughly 1 s.
Expected behavior
Since both RestClient.Execute() and RestClient.ExecuteAsync().Result are synchronous, both should have the exact same performance.
Calling RestClient.Execute() is not quite the same as calling RestClient.ExecuteAsync().Result. RestClient.Execute() currently uses a CustomSynchronizationContext in an attempt to do async in a synchronous manner. This implementation currently does not work as expected. In WinForms applications, calling RestClient.Execute() from the UI thread currently causes a deadlock. RestClient.ExecuteAsync().Result can also cause a deadlock in these cases.
I'd be interested to see what your results are if you compare it against Task.Run(() => RestClient.ExecuteAsync()).GetAwaiter().GetResult() as this is my proposed change to avoid deadlocks. This avoids the deadlock at the cost of using one thread pool thread per synchronous call.
Accessing Task.Result can result in a deadlock for a WinForms or WPF application, or in a Classic ASP.NET web application. Excessively calling Task.Result in an ASP.NET Core application can lead to a deadlock due to thread starvation of the thread pool. Using Task.Run to run an async method can lead to the same result. Awaiting the async version of the method is undoubtedly the best option.
Activity
tyuah8 commentedon Apr 27, 2026
Is this related to #2372?
deAtog commentedon Apr 30, 2026
Calling RestClient.Execute() is not quite the same as calling RestClient.ExecuteAsync().Result. RestClient.Execute() currently uses a CustomSynchronizationContext in an attempt to do async in a synchronous manner. This implementation currently does not work as expected. In WinForms applications, calling RestClient.Execute() from the UI thread currently causes a deadlock. RestClient.ExecuteAsync().Result can also cause a deadlock in these cases.
I'd be interested to see what your results are if you compare it against Task.Run(() => RestClient.ExecuteAsync()).GetAwaiter().GetResult() as this is my proposed change to avoid deadlocks. This avoids the deadlock at the cost of using one thread pool thread per synchronous call.
tyuah8 commentedon Apr 30, 2026
I assume the deadlocking is only when used in WinForms and there is no such issue in ASP.NET Core?
deAtog commentedon Apr 30, 2026
Accessing Task.Result can result in a deadlock for a WinForms or WPF application, or in a Classic ASP.NET web application. Excessively calling Task.Result in an ASP.NET Core application can lead to a deadlock due to thread starvation of the thread pool. Using Task.Run to run an async method can lead to the same result. Awaiting the async version of the method is undoubtedly the best option.