Skip to content

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):

var summary = await account.AccountSummaryAsync();  // βœ… Recommended

Use Sync version (rare specific scenarios):

var summary = account.AccountSummary();  // ⚠️ Only when necessary


πŸ“Š 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

βœ… 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 CancellationToken for long-running operations
  • Use async Main in console apps (C# 7.1+)

❌ DON'T:¢

  • Don't use .Result or .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ΒΆ


πŸ’‘ 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! πŸš€