Khác nhau giữa lập trình bất đồng bộ và đa luồng trong c#?

Em đang tìm hiểu về lập trình bất đồng bộ. Nhưng có xem 1 trang nói là bất đồng bộ được sinh ra từ đa luồng.
Anh(Chị) nào rành giải thích hộ em.

Đa luồng (multithreading) tức là có nhiều luồng (thread) được execute một cách độc lập, không bị phụ thuộc vào nhau về mặt thứ tự. Thay vì execute câu lệnh A -> B -> C thì cả A, B và C đều được execute đồng thời và kết quả của mỗi câu lệnh có thể trả về bất cứ lúc nào.

Bất đồng bộ (asynchronous) là một kiểu lập trình sao cho nhiều tác vụ cùng execute một lúc dựa trên multithreading.

Bạn có thể hiểu multithreading là công cụ, còn asynchronous là cách người ta dùng công cụ đó. Cho nên không thể so sánh giữa hai khái niệm này được.

3 Likes

Có n công việc.

Đa luồng là cho 2 đến n công việc được xử lý 1 lúc.

Bất đồng bộ là xử lý 1 đến n công việc 1 lúc. Việc đến trước sẽ xử lý trước, mọi thao tác xử lý phải ngay lập tức trả về giá trị (sẵn sàng/chưa sẵn sàng). Nếu sẵn sàng thì xử lý dữ liệu nhận được, nếu chưa thì để đó và xử lý việc khác. Async có thể hoạt động single thread, hoặc multithread.

2 Likes

Bất đồng bộ (Asynchronous), tức là bạn làm việc với các task, trong đó 1 task bạn có thể cắt nhỏ nó ra làm nhiều task và cho chạy xen kẽ với các task khác. Việc bạn đặt callback/await chính là cắt nhỏ task. Ở đây tuy tên nó là “bất đồng bộ” nhưng cái cách người ta hiểu “bất đồng bộ” ở đây có góc nhìn khác.
Đa luồng/đa tuyến (Multithreading), tức là task thực sự chạy song song với nhau trên logic.

Ví dụ:
Cho 2 task sau:

// Task A
async Task<int> getDataAndSendData()
{
    // Task A1
    Console.Write("start");
    var data = await getData();
    // Task A2
    Console.Write(data);
    await sendData(data);
    // Task A3
    Console.Write("done");
    return 0;
}
// Task B
async Task<int> login(username, password)
{
    // Task B1
    var result = await loginAndGetToken(username, password);
    // Task B2
    return result;
}
// Bật cả 2 task
static void Main(string[] args)
{
    getDataAndSendData();
    loginAndGetToken();
}

Tùy vào scheduler của Dotnet Framework, hoặc những chỗ await trả về nhanh cỡ nào, thứ tự các task có thể là:

image

hoặc là

image

(v.v.)

Nếu kết hợp với multithread thì có thể ném đống task này vào nhiều thread, nếu là 2 thread thì có thể là:

image

mà cũng có thể là:

image

Ghi chú: đặt await/callback không phải là cách duy nhất để cắt task.

3 Likes

Giả sử nhà hàng như là 1 chương trình webserver, Tiểu nhị như là main thread.
Thì tiểu nhị sẻ nhận order từ khách (chương trình nhận các request), sau đó tiểu nhị sẻ đưa order cho nhà bếp làm các món ăn (thực thi database - thêm sửa xóa).
Rồi trong khi đầu bếp đang làm món ăn thì tiểu nhị có thể nhận tiếp order ( nhận tiếp request mà ko cần phải trả lại response của request trước).
Sau khi nhà bếp chế xong món ăn thì nhà bếp sẻ kêu ông tiểu nhị lại chạy đến để lấy món ăn giao cho khách (trả response cho request số 1).
Đó tức là async bản chất cũng là đa luồng tuy nhiên dễ kiểm soát hơn.

1 Like

Async không nhất thiết phải cần tới multithread nhé.

như ví dụ ở trên của m thì nó là 1 thread của C# (tiểu nhị) là 1 số thread của database C++ (đầu bếp). Chứ làm sao 1 thread làm đc nhiều việc cùng lúc được , thread của C# nó giao việc cho thằng C++ làm hộ nó để nó đi làm việc khác :smiley:

Ở đây mình cần làm rõ là bản thân async nó không đồng nhất với multithread, nó khác với việc người ta dùng nó để làm hệ thống điều phối cho những hệ thống khác như db.

Thì theo mình bản chất của nó vẫn là multithread, và khi code thì mình chỉ cần quan tâm main thread, và main thread giao việc cho các thread khác làm và khi nào xong thì ới main thread lại nhận kết quả.
Chủ thớt có google đc câu này theo m nghĩ cũng ko sai.

Có vẻ Async giống với đa nhiệm trong hệ điều hành hơn. Có thể cài đặt hệ điều hành chạy trên 1 core cpu, 1 core đó có thể xử lý nhiều tác vụ, thread gần như cùng lúc (nghe nhạc khi đang lướt web, đại loại thế) thì Async cũng vậy, mỗi task trong Async là 1 green thread giống với thread của hệ điều hành, chỉ cần 1 thread(process) là đủ để chạy cả hệ thống async đó.

Còn khi async được chạy trên nhiều thread, các green thread kia được lập lịch để chạy cùng lúc trên các thread.

=> Ok, bản chất Async là multithreaded, nhưng là green thread.

có 1 ví dụ mà nó kết hợp cả asynchronous và multithreaded, đó là nodejs. Main thread là javascript chạy asynchronous single thread nhưng phía sau nó là các thread khác phục vụ i/o.

p/s: Hầu hết các môi trường xử lý bất đồng bộ không chạy multithreaded, chúng chạy concurrency khi có khả năng (vd trong máy multicore).

3 Likes

Ok fine, các bác lập trình ở mức high level thì cho rằng async là multithread cũng… chả sao, nhưng nếu các bác thực sự lập trình với thread, các bác sẽ hiểu.
Trang này có hình vẽ đẹp hơn của mình: https://codewala.net/2015/07/29/concurrency-vs-multi-threading-vs-asynchronous-programming-explained/

2 Likes

Mình không thực sự chuyên về kiến trúc máy tính nên có thể comment này sẽ hơi ngu. Nhưng với những gì các b comment về thread/async làm m càng thấy khó hiểu và mập mờ hơn.

Đoạn này rất khó hiểu! 1 core chỉ xử lý 1 tác vụ tại 1 thời điểm và tại sao async lại giống với đa nhiệm trong hệ điểu hành hơn so với đa nhiệm trong???

Tại sao bản chất của async lại là multithreading??? hay cụ thể là green threads? M chưa nghĩ được analogy nào để kết nối cả.

Nếu async hoạt động như vậy thì chẳng phải nó đã được synchorinzed rồi.

1 Like

Mình cũng không chuyên về nó, mình chỉ cố gắng giải thích bằng cách hiểu của mình thôi.

Ok, các hệ điều hành đầu tiên ra đời là đơn nhiệm. 1 thời điểm chỉ chạy được 1 phần mềm.
Sau đó, hệ điều hành đa nhiệm ra đời, cùng một thời điểm có thể xử lý rất nhiều phần mềm.

Hệ điều hành đa nhiệm (HDH) thực tế chạy nhờ vào cpu, cpu chỉ có vài core nhưng nó phải xử lý rất nhiều process (hoặc đơn giản là phần mềm) cùng lúc. Thread ra đời để dễ dàng làm việc đó. Mỗi process là tập hợp của các câu lệnh riêng rẽ, trình scheduler của hệ điều hành sẽ điều phối các câu lệnh đến cpu để xử lý. Việc này diễn ra rất nhanh nên không thể nhận thấy, suy ra, các được xử lý gần như cùng 1 lúc.

  • sorry mình không dùng windows để có thể mở task manager

Synchronous: tất cả phần mềm đều được hệ điều hành xử lý các câu lệnh từ trên xuống dưới.

VD: Khi đọc 1 tcpstream, nếu gọi lệnh đọc khi chưa có dữ liệu trong đó, câu lệnh sẽ loop đến khi nào đọc được thì trả về dữ liệu. Trong quá trình đọc dữ liệu (Blocking), không lệnh nào khác trong thread này được xử lý.

Nếu đã lập trình arduino thì sẽ biết đến hàm delay(int), nó dừng chương trình lại 1 khoảng thời gian. Khi đó vi xử lý không xử lý bất kỳ câu lệnh nào của bạn.

Vì yêu cầu phải xử lý nhiều câu lệnh 1 lúc, multithreaded ra đời. Khi cần chỉ việc spawn 1 thread, và hệ điều hành sẽ làm nốt công việc xử lý của nó synchronous. Nhưng 1 thread của hệ điều hành quá thừa thãi, nó cung cấp quá nhiều tài nguyên máy tính để làm 1 công việc đơn giản nên spawn nhiều thread không khả thi.

Asynchronous: yêu cầu xử lý rất nhiều công việc 1 lúc vẫn còn đấy. Các các lệnh, hàm, stream,… có thể tốn thời gian block được cải tiến thêm chế độ non-blocking. Khi này, ví dụ việc gọi hàm đọc 1 stream sẽ được trả về ngay lập tức, nếu có dữ liệu thì trả về dữ liệu, chưa có thì trả về 1 giá trị khác như enum Error::WouldBlock chẳng hạn. Việc này không block thread nên ngay sau đó bạn có thể làm việc khác.

Tuy nhiên, nếu chỉ có như này thì việc viết phần mềm trở nên phức tạp hơn, lập trình viên phải làm nhiều việc khác hơn là chú trọng vào logic của phần mềm. Các library, framework ra đời giúp lập trình viên viết phần mềm xử lý bất đồng bộ nhưng ẩn đi những công việc như trên mà đáng lẽ ra lập trình viên phải làm. Trung tâm của các framework này có thể là 1 event loop. Việc đọc stream và xử lý của bạn giờ chỉ là ném vào event loop cái(những) stream(s) muốn đọc, callback xử lý dữ liệu đọc được. event loop khi start sẽ block thread và gọi hàm đọc stream của từng stream, nếu stream trả về dữ liệu thì gọi hàm callback ứng với stream đó (Thực tế có thể có nhiều phương pháp khác và nhiều thứ phức tạp khác). Đây chỉ là ví dụ cho việc đọc 1 stream thôi, có rất nhiều ví dụ khác liên quan đến ghi, timer, …

Asynchronous giúp tránh phải block nhiều thread.

Green thread: Lập trình asynchronous nói chung là phức tạp, không dễ như synchronous nên green thread là 1 lớp abtraction giúp lập trình asynchronous giống như lập trình multithread synchronous vậy. Green thread giống như thread của hệ điều hành nhưng nó không vận hành trực tiếp bởi hệ điều hành mà bởi framework hay các máy ảo. Phần này mình không tìm hiểu nhiều, mong là đúng.

Async giống với đa nhiệm trong hệ điều hành không đúng lắm, mình lấy đây làm ví dụ để có thể dễ liên tưởng thôi. 1 core không xử lý tác vụ mà xử lý từng câu lệnh như ảnh phía trên:

2 Likes

Vậy thì rất khó để m và b có thể tìm hiểu chính xác trừ khi có 1 người thực sự hiểu rõ giải thích, hy vọng có cao nhân đi qua. Tuy nhiên m vẫn có 1 vài điểm nhỏ từ trả lời của b mà m thấy chưa hoàn toàn đúng.

Điều này là không chính xác, nếu 1 thread giữ CPU quá lâu nó “có thể” bị kernel gửi vào trạng thái chờ và chuyển quyền sử dụng CPU cho thread khác, “có thể” ở đây phụ thuộc vào thread (process) scheduler scheme, quá trình chuyển trạng thái được gọi là context switch và mỗi hệ điều hành có 1 cs mechanism riêng.

Ví dụ đơn giản nhất là ngay trên CPU 1 core, bạn vừa có thể download (blocking) và serve nginx tại cùng 1 thời điểm mặc dù download task là I/O bound.

M chưa bao giờ làm Arduino nhưng GG thì thấy nó không hỗ trợ thread mặc dù vẫn có thể simulate nó.

Mình chỉ đưa link rồi trùm chăn hóng các bác thảo luận thôi. Vì mình đâu có biết C# đâu :yum:

2 Likes

Đa luồng : Nói về việc chương trình tạo ra nhiều hơn 1 luồng xử lý. Tất nhiên thế thì nó sẽ bất đồng bộ.

Bất đồng bộ: Nói về việc xử lý không theo 1 thứ tự trước sau. Để làm được điều đó trong C# thì không nhất thiết đa luồng.

có trường hợp món ăn của resquest thứ 2 trả trước mà ông đầu bếp đang làm món 1. Thì có phải đầu bếp phải dừng món 1 khi món 2 được gọi ko bạn, thanks.

Trong khu vực bếp có nhiều đầu bếp mà bạn có phải có 1 ông đâu

2 Likes

Thấy các bác có vẻ bối rối giữa multithread và async, mình xin share 1 link mà mình note lại đã lâu. Tự thấy trình chưa đủ để giải thích cặn kẽ nên chỉ share link thôi.
http://blog.stephencleary.com/2013/11/there-is-no-thread.html

1 Like

Async trên 1 thread có lẽ giống với goto, nhưng m thấy khá vô dụng và gần nhưng trong thực thế m ko thấy ma nào dùng kiểu đó cả @@, ví dụ bạn đang nhặt rau dở mà chạy đi vặt lông vịt thì chốt lại bạn vẫn phải làm cả 2 việc với cùng 1 lượng thời gian.

1 Like
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?