Synchronous vs Asynchronous Methods - When to Use WhatΒΆ
Every method in MT5Account has two versions:
Async(e.g.,AccountSummaryAsync) and Sync (e.g.,AccountSummary). This guide explains when to use each.
π― Quick AnswerΒΆ
Use Async version (99% of cases):
Use Sync version (rare specific scenarios):
π Side-by-Side ComparisonΒΆ
| Aspect | Async Version (await MethodAsync()) |
Sync Version (Method()) |
|---|---|---|
| Thread blocking | β Does NOT block thread | β Blocks calling thread |
| Performance | β Better (thread pooling) | β Worse (one thread per call) |
| Scalability | β High (1 thread = 1000s of operations) | β Low (1 thread = 1 operation) |
| Responsiveness | β UI stays responsive | β UI freezes during call |
| Memory usage | β Lower (fewer threads) | β Higher (more threads) |
| Deadlock risk | β Lower | β Higher (especially in UI) |
| Recommended by | β Microsoft, .NET guidelines | β οΈ Legacy compatibility only |
| When to use | Almost always | Very specific scenarios |
π When to Use ASYNC (Recommended)ΒΆ
β Use Case 1: ASP.NET / Web APIsΒΆ
// Web API Controller
[HttpGet("balance")]
public async Task<ActionResult<double>> GetBalance()
{
// β
Async frees thread for other requests
var summary = await _mt5Account.AccountSummaryAsync();
return Ok(summary.AccountBalance);
}
Why: In web servers, threads are expensive. Async allows one thread to handle thousands of concurrent requests.
Result:
- π 100 threads can handle 10,000+ concurrent requests
- Without async: 100 threads = only 100 concurrent requests
β Use Case 2: Desktop UI (WPF, WinForms, Avalonia)ΒΆ
// Button click handler
private async void OnCheckBalanceClick(object sender, EventArgs e)
{
// β
UI thread stays responsive
var summary = await _mt5Account.AccountSummaryAsync();
BalanceLabel.Text = $"Balance: ${summary.AccountBalance}";
}
Why: Async keeps UI responsive. User can still interact while waiting for MT5 response.
Result:
- β UI doesn't freeze
- β User can cancel operation
- β Better user experience
β Use Case 3: Trading Bots / Strategy ExecutionΒΆ
// Trading strategy
public async Task ExecuteStrategyAsync()
{
// β
Can monitor multiple symbols concurrently
var tasks = symbols.Select(async symbol =>
{
var tick = await _mt5Account.SymbolInfoTickAsync(symbol);
return AnalyzeSignal(tick);
});
var signals = await Task.WhenAll(tasks);
}
Why: Process multiple symbols in parallel without blocking threads.
Result:
- π 10 symbols analyzed in ~1 second (concurrent)
- Without async: 10 symbols = ~10 seconds (sequential)
β Use Case 4: Real-Time StreamingΒΆ
// Real-time tick monitoring
await foreach (var tick in _mt5Account.OnSymbolTickAsync(symbols, cancellationToken))
{
// β
Non-blocking stream processing
ProcessTick(tick);
}
Why: Streaming is inherently asynchronous. Can't do it with sync methods.
Result:
- β Continuous data flow
- β Cancellable streams
- β No thread blocking
β οΈ When to Use SYNC (Rare Cases)ΒΆ
π‘ Use Case 1: Console Applications (Quick Scripts)ΒΆ
// Simple one-off script
static void Main()
{
var account = new MT5Account(...);
account.Connect();
// β
Acceptable for simple scripts
var summary = account.AccountSummary();
Console.WriteLine($"Balance: {summary.AccountBalance}");
}
Why: For simple scripts that run once and exit, blocking is acceptable.
When acceptable:
- Script runs once and exits
- No UI, no web server
- Not performance-critical
- Note: Even here, async Main is better (C# 7.1+)
Better alternative:
static async Task Main()
{
var account = new MT5Account(...);
await account.ConnectAsync();
var summary = await account.AccountSummaryAsync(); // Still better!
Console.WriteLine($"Balance: {summary.AccountBalance}");
}
π‘ Use Case 2: Legacy Code IntegrationΒΆ
// Old library that doesn't support async
public class LegacyTradingSystem
{
private MT5Account _account;
// Old interface - can't change signature
public double GetBalance()
{
// β οΈ Forced to use sync version
return _account.AccountSummary().AccountBalance;
}
}
Why: Existing codebase can't be modified to support async/await.
When acceptable:
- Third-party library constraints
- Can't change method signatures
- Gradual migration to async
Better alternative:
- Wrap in async layer when possible
- Plan migration to async API
π‘ Use Case 3: Synchronous ConstructorsΒΆ
public class TradingContext
{
public double InitialBalance { get; }
public TradingContext(MT5Account account)
{
// β Can't use async in constructor
// β οΈ Forced to use sync version
InitialBalance = account.AccountSummary().AccountBalance;
}
}
Why: C# constructors can't be async.
When acceptable:
- Initialization code in constructors
- Static initializers
Better alternative:
public class TradingContext
{
public double InitialBalance { get; private set; }
private TradingContext() { }
// β
Static async factory method
public static async Task<TradingContext> CreateAsync(MT5Account account)
{
var context = new TradingContext();
var summary = await account.AccountSummaryAsync();
context.InitialBalance = summary.AccountBalance;
return context;
}
}
π‘ Use Case 4: Unit Tests (Rare)ΒΆ
[Test]
public void TestAccountBalance()
{
var account = CreateMockAccount();
// β οΈ Some test frameworks don't support async tests well
var balance = account.AccountSummary().AccountBalance;
Assert.AreEqual(10000.0, balance);
}
Why: Some older test frameworks have poor async support.
Better alternative:
[Test]
public async Task TestAccountBalance()
{
var account = CreateMockAccount();
// β
Modern test frameworks support async
var summary = await account.AccountSummaryAsync();
Assert.AreEqual(10000.0, summary.AccountBalance);
}
β When NOT to Use SYNC (Common Mistakes)ΒΆ
β Mistake 1: UI Thread BlockingΒΆ
// β WRONG - Freezes UI!
private void OnButtonClick(object sender, EventArgs e)
{
var summary = _account.AccountSummary(); // UI freezes here!
BalanceLabel.Text = $"${summary.AccountBalance}";
}
// β
CORRECT - UI stays responsive
private async void OnButtonClick(object sender, EventArgs e)
{
var summary = await _account.AccountSummaryAsync();
BalanceLabel.Text = $"${summary.AccountBalance}";
}
β Mistake 2: Deadlock in ASP.NETΒΆ
// β WRONG - Can cause deadlock!
[HttpGet("balance")]
public ActionResult<double> GetBalance()
{
var summary = _account.AccountSummary(); // Deadlock risk!
return Ok(summary.AccountBalance);
}
// β
CORRECT - No deadlock
[HttpGet("balance")]
public async Task<ActionResult<double>> GetBalance()
{
var summary = await _account.AccountSummaryAsync();
return Ok(summary.AccountBalance);
}
β Mistake 3: Poor ScalabilityΒΆ
// β WRONG - Blocks 10 threads!
var results = symbols.Select(symbol =>
{
return _account.SymbolInfoTick(symbol); // Each call blocks a thread
}).ToList();
// β
CORRECT - Concurrent, no blocking
var tasks = symbols.Select(symbol =>
_account.SymbolInfoTickAsync(symbol)
);
var results = await Task.WhenAll(tasks);
π¬ Technical Deep DiveΒΆ
How Async WorksΒΆ
// When you call:
var summary = await account.AccountSummaryAsync();
// What happens:
// 1. Thread sends gRPC request to MT5
// 2. Thread is RELEASED back to thread pool (can handle other work)
// 3. When MT5 responds, ANY available thread picks up the result
// 4. Execution continues after 'await'
Key point: Thread doesn't wait idle. It's freed to do other work.
How Sync WorksΒΆ
// When you call:
var summary = account.AccountSummary();
// What happens:
// 1. Thread sends gRPC request to MT5
// 2. Thread BLOCKS and waits (can't do anything else)
// 3. When MT5 responds, same thread continues
// 4. Thread was wasted during wait time
Key point: Thread is blocked and wasted during I/O wait.
π Performance ImpactΒΆ
Scenario: Web API with 1000 concurrent requestsΒΆ
Using Async:
Threads needed: ~10-20
Memory usage: ~50 MB
Response time: ~100ms average
Result: β
All 1000 requests handled smoothly
Using Sync:
Threads needed: 1000
Memory usage: ~1 GB
Response time: ~500ms average (thread starvation)
Result: β Server crashes or rejects requests
π Best PracticesΒΆ
β DO:ΒΆ
- Use async/await for ALL I/O operations (MT5 calls, database, HTTP)
- Use
ConfigureAwait(false)in libraries (not in UI code) - Always pass
CancellationTokenfor long-running operations - Use async Main in console apps (C# 7.1+)
β DON'T:ΒΆ
- Don't use
.Resultor.Wait()on async methods (causes deadlocks) - Don't mix sync and async code unnecessarily
- Don't use sync methods in UI threads
- Don't use sync methods in ASP.NET controllers
π Method Naming ConventionΒΆ
All methods follow this pattern:
| Pattern | Example | When to Use |
|---|---|---|
MethodAsync() |
AccountSummaryAsync() |
Default choice (99% of cases) |
Method() |
AccountSummary() |
Rare specific scenarios only |
Suffix Async = This method is asynchronous (recommended)
No suffix = This method is synchronous (compatibility only)
π Real-World ExamplesΒΆ
Example 1: Trading Bot (Async - Correct)ΒΆ
public class TradingBot
{
private readonly MT5Account _account;
public async Task RunAsync(CancellationToken cancellationToken)
{
// β
Monitors multiple streams concurrently
await foreach (var tick in _account.OnSymbolTickAsync(_symbols, cancellationToken))
{
// Analyze tick
var signal = await AnalyzeTickAsync(tick);
if (signal.ShouldTrade)
{
// Place order
await _account.OrderSendAsync(CreateOrder(signal));
}
}
}
}
Example 2: Price Monitor Dashboard (Async - Correct)ΒΆ
public class PriceMonitor
{
public async Task<List<SymbolPrice>> GetAllPricesAsync(string[] symbols)
{
// β
Fetch all symbols concurrently (parallel)
var tasks = symbols.Select(async symbol =>
{
var tick = await _account.SymbolInfoTickAsync(symbol);
return new SymbolPrice
{
Symbol = symbol,
Bid = tick.Bid,
Ask = tick.Ask,
Time = tick.Time.ToDateTime()
};
});
return (await Task.WhenAll(tasks)).ToList();
}
}
Performance:
- Async: Fetches 100 symbols in ~1 second (parallel)
- Sync: Fetches 100 symbols in ~100 seconds (sequential)
π― Decision TreeΒΆ
Need to call MT5 method?
β
ββ Is this a UI application?
β ββ YES β Use Async (keeps UI responsive)
β
ββ Is this a web application/API?
β ββ YES β Use Async (better scalability)
β
ββ Is this a trading bot/long-running service?
β ββ YES β Use Async (better performance)
β
ββ Is this a simple one-off script?
β ββ YES β Use Async Main (C# 7.1+) OR Sync if really necessary
β
ββ Are you forced by legacy constraints?
ββ YES β Use Sync temporarily, plan migration to Async
π Further ReadingΒΆ
- Microsoft: Async/Await Best Practices
- Stephen Cleary: Don't Block on Async Code
- Task-based Asynchronous Pattern (TAP)
π‘ SummaryΒΆ
| Question | Answer |
|---|---|
| Which should I use? | Async in 99% of cases |
| Why Async? | Better performance, scalability, responsiveness |
| When is Sync okay? | Simple scripts, legacy integration, constructors (rare) |
| Main rule? | If doing I/O (network, disk, database) β use Async |
Remember: Async is not harder, it's just different. Once you understand async/await, you'll never want to go back to blocking code! π