Sobes.tech
Middle+

Μπορεί κανείς να καλέσει ασύγχρονο κώδικα συγχρονικά;

sobes.tech AI

Απάντηση από AI

Ναι, είναι δυνατόν, αλλά μπορεί να προκαλέσει προβλήματα, ιδίως deadlocks.

Τρόποι κλήσης ασύγχρονου κώδικα με συγχρονικό τρόπο:

  1. Χρήση της ιδιότητας Result ή της μεθόδου Wait() σε ένα αντικείμενο Task.

    // Συγχρονική κλήση μιας ασύγχρονης μεθόδου
    Task.Run(async () => await SomeAsyncTask()).Wait();
    
    // Λήψη αποτελέσματος μιας ασύγχρονης μεθόδου συγχρονικά
    var result = SomeAsyncTaskWithResult().Result;
    
    async Task SomeAsyncTask()
    {
        await Task.Delay(100);
    }
    
    async Task<int> SomeAsyncTaskWithResult()
    {
        await Task.Delay(100);
        return 42;
    }
    
  2. Χρήση της GetAwaiter().GetResult():

    // Συγχρονική κλήση μιας ασύγχρονης μεθόδου με λήψη αποτελέσματος
    var result = SomeAsyncTaskWithResult().GetAwaiter().GetResult();
    

Μειονεκτήματα αυτής της προσέγγισης:

  • Deadlock: Το πιο συνηθισμένο πρόβλημα, ειδικά σε UI νήματα ή σε περιβάλλοντα ASP.NET με συγχρονιστικό πλαίσιο. Το Wait() και το Result μπλοκάρουν το τρέχον νήμα, περιμένοντας την ολοκλήρωση της εργασίας. Αν η ασύγχρονη μέθοδος προσπαθεί να πιάσει το μπλοκαρισμένο πλαίσιο συγχρονισμού για να συνεχίσει μετά το await, προκύπτει deadlock.
  • Μείωση απόδοσης και κλιμάκωσης: Το μπλοκάρισμα νημάτων αντί να χρησιμοποιούνται ασύγχρονες λειτουργίες καταναλώνει περισσότερους πόρους και μειώνει την ικανότητα της εφαρμογής να χειρίζεται πολλαπλά αιτήματα ταυτόχρονα (π.χ., σε web εφαρμογές).
  • Μη προφανής συμπεριφορά: Ο συνδυασμός ασύγχρονου και συγχρονου κώδικα μπορεί να κάνει τη ροή εκτέλεσης δύσκολη στην κατανόηση και αποσφαλμάτωση.

Στις περισσότερες περιπτώσεις, η σωστή προσέγγιση είναι η χρήση του await για την συνέχιση της ασύγχρονης εκτέλεσης ή, αν χρειάζεται να εκτελέσετε ασύγχρονο κώδικα στο σημείο εισόδου της εφαρμογής (π.χ., Main), η χρήση του Task.Run() σε συνδυασμό με Wait() ή η καθιστώντας το Main ασύγχρονο, αν η έκδοση C# το υποστηρίζει (async Task Main).

Παράδειγμα ασφαλούς κλήσης ασύγχρονου κώδικα από μια συγχρονική μέθοδο, χρησιμοποιώντας Task.Run για μεταφορά της εκτέλεσης στο pool νημάτων:

// Ασφαλής κλήση ασύγχρονης μεθόδου από συγχρονικό πλαίσιο
Task.Run(async () =>
{
    await SomeAsyncTask();
}).Wait();

// Ασφαλής κλήση ασύγχρονης μεθόδου με αποτέλεσμα
var result = Task.Run(async () =>
{
    return await SomeAsyncTaskWithResult();
}).Result;