⚡ Asynchronous in C#

1️⃣ Task


1.1 Task không có kiểu trả về

  • Task nhận vào một Action không có param:
Task t2 = new Task(Action);

  • Task nhận vào một Action có param, đối số thứ 2 là giá trị truyền vào Action:
Task t3 = new Task(Action<object>, object);

  • Chờ tất cả task hoàn thành trước khi gọi lệnh tiếp theo:
t2.Wait();
t3.Wait();
Console.WriteLine("Press any key");

  • Cách ngắn gọn dùng Task.WaitAll():
Task.WaitAll(t2, t3);

  • Ví dụ chức năng Task:
static Task Task2()
{
    Task t2 = new Task(() =>
    {
        // Do something
    });

    t2.Start();
    t2.Wait();
    Console.WriteLine("T2 Finish");
    return t2;
}

static Task Task3()
{
    Task t3 = new Task(() =>
    {
        // Do something
    });

    t3.Start();
    t3.Wait();
    Console.WriteLine("T3 Finish");
    return t3;
}

Task t2 = Task2();
Task t3 = Task3();

💡 Lưu ý: Dù task chạy trên nhiều luồng khác nhau, việc gọi Wait() khiến chương trình thực thi giống đồng bộ.


1.2 Task có kiểu trả về

  • Task trả về giá trị string, không có tham số đầu vào:
Task<string> t4 = new Task<string>(() =>
{
    // Do something
    return "Result from T4";
});

  • Task trả về string, có tham số đầu vào:
Task<string> t5 = new Task<string>((object obj) =>
{
    string t = (string)obj;
    return $"Result from {t}";
}, "T5");

  • Thực thi và lấy kết quả:
t4.Start();
t5.Start();
Task.WaitAll(t4, t5);

var resultT4 = t4.Result;
var resultT5 = t5.Result;
Console.WriteLine("Press any key");

  • Chuyển thành function con:
static Task<string> Task4()
{
    Task<string> t4 = new Task<string>(() =>
    {
        return "Result from T4";
    });
    t4.Start();
    return t4;
}

static Task<string> Task5()
{
    Task<string> t5 = new Task<string>((object obj) =>
    {
        string t = (string)obj;
        return $"Result from {t}";
    }, "T5");
    t5.Start();
    return t5;
}

Task<string> t4 = Task4();
Task<string> t5 = Task5();

Task.WaitAll(t4, t5);
var resultT4 = t4.Result;
var resultT5 = t5.Result;
Console.WriteLine("Press any key");


2️⃣ Từ khóa Async & Await

  • Khi dùng async/await:
    • await t2 hay await t3 sẽ đợi task hoàn thành trước khi chạy statement tiếp theo.
    • Không khóa thread chính, task vẫn chạy trên các luồng riêng, kết quả luân phiên nhau.

2.1 Đối với Task không có kiểu trả về

static async Task Task2()
{
    Task t2 = new Task(() =>
    {
        // Do something
    });

    await t2;
    Console.WriteLine("T2 Finish");
}

static async Task Task3()
{
    Task t3 = new Task(() =>
    {
        // Do something
    });

    t3.Start();
    await t3;
    Console.WriteLine("T3 Finish");
}


2.2 Đối với Task có kiểu trả về

static async Task<string> Task4()
{
    Task<string> t4 = new Task<string>(() =>
    {
        // Do something
        return "Result from T4";
    });
    t4.Start();
    var result = await t4;
    Console.WriteLine("T4 Finish");
    return result;
}

static async Task<string> Task5()
{
    Task<string> t5 = new Task<string>((object obj) =>
    {
        string t = (string)obj;
        return $"Result from {t}";
    }, "T5");
    t5.Start();
    var result = await t5;
    Console.WriteLine("T5 Finish");
    return result;
}

// Gọi thực thi
Task<string> t4 = Task4();
Task<string> t5 = Task5();

// Lấy kết quả
var resultT4 = await t4;
var resultT5 = await t5;

Console.WriteLine("Press any key");

💡 Key Point: async + await giúp:

  • Chờ task hoàn thành nhưng không block main thread.
  • Task chạy độc lập, statement tiếp theo chỉ thực thi khi task hoàn thành.
  • Phù hợp cho cả Task có kiểu trả về hoặc không có kiểu trả về.