Thắc mắc về asyn/await
TIL
737
White

Kim Long Dinh viết ngày 19/05/2019

Câu hỏi:

  1. Có phải asyn/await dùng ThreadPool (quản lý nhiều separate thread), nếu có thì liệu rằng nhiều thread thực thi cùng lúc như vậy chương trình có nhanh hơn không (mình đang hình dung google chrome nguốn RAM nhưng user ngày nay lại thích thực thi nhiều tác vụ cùng lúc thay vì phải chờ đợi)?

  2. Trong team mình có anh kia đã từng dùng tới asyn/await khi thao tác dữ liệu lớn, nhưng sau đó a leader có nói dùng cái đó trong trường hợp này là không khả thi (mình review code thì thấy kết quả trả về không hề có sự phụ thuộc lẫn nhau (độc lập)) => vậy thì theo ý kiến cá nhân mình thấy nếu câu trả lời cho số 1 là YES thì thực thi sẽ nhanh hơn, đáp ứng tốt hơn cho user về mặt UX, còn mọi người thấy vì yếu tố nào khác mà mình không nên dùng tới asyn/await trong trường hợp này không nhỉ?

Gợi ý:

  • Theo như kiến thức của mình thì việc async/await là để tối ưu hoá threadpool. Khi chương trình cần phải làm tác vụ gì đó lâu mà không phải do mình kiểm soát (truy xuất DB, lấy dữ liệu từ 3rd party) thay vì phải ngồi đợi và khiến thread đang chạy bị "giam" thì nó sẽ giải phóng thread đó về lại pool để có thể nhận request mới.

Rồi khi có kết quả thì 1 thread mới sẽ được chỉ định để tiếp tục công việc. Việc này tối ưu hoá được threadpool vì số lượng thread có hạn. Nên nếu là mình thì mình cũng sẽ dùng await/async như anh senior của bạn

  • Tưởng tượng bạn cần lấy 100k dòng trong DB, nó mất 10s. Nếu như cách bình thường List a = getData() thì cái thread chạy dòng này sẽ bị block trong 10s. Và nếu có 100 request như vậy đến, 100 thread sẽ bị "block". Và request thứ 101 sẽ ko được thực thi.

Ngược lại khi dùng List a = await getData() thì ngay tại thời điểm chờ 10s cho DB trả về dữ liệu, hàm chứa dòng lệnh này sẽ bị hoãn (suspend) và thread này được trả về pool, nếu có request kế tiếp đến thì thread vừa trả về vẫn có thể dùng để thực thi cho đến đoạn lấy dữ liệu ở trên. Như vậy bạn có thể serve nhiều hơn 100 request với cách này. Cái await nó buộc chương trình phải chờ kết quả rồi mới làm tiếp nên tất nhiên bạn thấy nó chờ.

Còn khi chỉ viết Task thì nó chỉ trả về 1 cái reference đến cái task thôi chứ nó không phải là kết quả.

Cuối cùng khi bạn muốn sử dụng kết quả bạn cũng phải dùng i.Result() hoặc await i (mà nếu lúc này kết quả vẫn chưa có thì vẫn phải chờ). Việc sử dụng async/await chỉ hữu dụng và thấy rõ khi bạn test thật nhiều request cùng lúc, chứ lúc debug chỉ gửi 1 request thì bạn tất nhiên không thấy sự khác biệt rồi

  • Đầu tiên hết là mình có .net threads. Để quản lý thread thì .net có threadpool.

Theo mình biết thì số lượng thread phụ thuộc vào CPU, số lượng địa chỉ memory, hệ điều hành...nhưng chia làm 2 loại worker và I/O. Một số sách nói chia vậy là để threadpool có thể handle I/O thậm chí cả khi nó không còn worker thread đang rảnh.

Threadpool có nhiều kỹ thuật để quản lý sử dụng thread, xem cái nào đang work, assign work cho cái nào...dùng mấy algorithm bla bla nhưng tóm lại là tự mình làm việc (một cách không hiệu quả) với thread rất là expensive - tốn tài nguyên. Nên .net cho ra đời Task. Task là một công việc. Mình có Task mình cứ giao cho ThreadPool nó sẽ tự manage và run sao cho hợp lý nhất. Mình có thể có 1 triệu Task nhưng tất cả số task đó đều được giao cho 1000 threads. Task không giống như thread rất là lightweight.

ThreadPool sẽ đóng vai trò quản lý sao cho tối ưu nhất. Từ Task mình có mong muốn để code asynchronous một cách dễ dàng hơn (ví dụ như chain 1 loạt Task continueWith hay với các methods khác của Task), trực quan hơn thì có feature async/await.

Nó là một phần của asynchronous programming có lợi ích là mình sử dụng tài nguyên hiệu quả hơn như anh ở trên nói, bạn có nhiêu đó threads, nếu 5 threads đểu đang busy (block) bởi tác vụ synchronous. Threadpool sẽ phải cân đối lại và (cơ bản) là tạo thêm threads để phục vụ những tác vụ khác mà như bạn biết, rất là expensive.

Thử tưởng tượng hàng trăm ngàn request mà tất cả các thread đều busy hết thì sẽ thế nào. ThreadPool (một cách đơn giản) sẽ kiểm tra các threads hiện tại, tạo threads mới, chạy thuật toán để assign tasks, quản lý state...v..v Từ asynchronous programming (cụ thể hơn là dùng async/await) bạn có thể handle dc nhiều thứ hơn và nhanh hơn.

Ví dụ như bạn có một service handle 100000 requests mà mỗi request gọi xuống DB hoặc HTTP sang một endpoint khác. Bạn sẽ thấy là async giúp handle được nhiều request hơn trong một cùng khoảng thời gian. Tài nguyên hữu hạn => Threadpool quản lý không dễ dãi (nhiểu thuật toán, nhiều limit) => nên dùng asynchronous.

Xa hơn asynchronous programming không chỉ gói gọn trong process của phần mềm của mình mà còn ở tầng kiến trúc nữa. Ví dụ như trong kiến trúc phân tán, mình có nhiều VM chạy trên một cluster thì mình cũng không muốn tất cả VM đều ngồi chờ đợi I/O từ bên ngoài (giả sử mỗi máy VM chạy 5 services đều cùng gọi 10 000 requests tới DB, HTTP, FTP và ...chờ).

Bạn có thể nhìn CPU của mình, giả sử có 4 core vật lý và thử tượng tượng 4 core ngồi không vì đang chờ máy in in 4 cuốn sách 100k trang vì một tập lệnh Wait() nào đó. Ngay cả hệ điều hành được làm ra cũng phải quản lý cân đối những core của CPU này, liên tục assign resource CPU và trả lại để mọi công việc mình giao cho nó nó đều phải hoàn tất, mà mình thì không thể tư assign thêm core cho CPU.

Tóm lại mình nghĩ 1) là YES, async có thể làm cho program chạy nhanh hơn nhưng là do nó xử dụng hiệu quả tài nguyên hơn để đạt được khả năng concurrency tốt hơn.

Về ý số 2 của bạn. Mình nghĩ nên có sự phân biệt giữa asynchronous và concurrency. Concurrency mới phụ thuộc vào việc mỗi request có độc lập hay không. Asynchronous thì theo mình không liên quan lắm. Nếu không độc lập thì bạn dùng async/await và nếu độc lập bạn cũng có thể dùng async/await. async/await bắt đầu bằng việc mình dùng keyword async trên method: ví dụ async Task GetNumber() { A(); B();}.

Nó cho .net biết đây là một method async và có thể await (awaitable)khi gọi. Khi thực thi, control đang chạy tới dòng await thì sẽ yield (nhả) control ra lên một level và sinh ra một state machine để handle kết quả công việc đó và lấy context để sau này tiếp tục nếu cần, cứ thế nó up ra hết thì sẽ nhả thread lại vào threadpool. Khi có kết quả thì sẽ handle tiếp bằng cách lấy lại context cũ và chạy tiếp các dòng tiếp theo. Phần này có thể chạy trên thread cũ cũng có thể là một thread khác.

Trường hợp mà mình nghĩ không nên dùng async là khi phần công việc đó là synchronous mà mình muốn wrap nó vào một async method vì như vậy không có ích lợi gì cả, còn có thể gây ra những hiểu sai về method mà mình viết và sau này maintain sẽ phức tạp hơn khi code lớn và nhiều team mà nó thì không đúng trong trường hợp 2) của bạn nên mình nghĩ dùng asynchronous là không sai cho riêng việc gọi xuống DB và lấy dữ liệu. Trừ phi trường hợp này bạn muốn cùng lúc thực hiện một hoặc nhiều những tác vụ khác không liên quan đến việc lấy data.

Ví dụ await GetData(); DisplayLoadingScreen(); LogAuditRequest(); 2 hàm sau không dùng hoặc không dùng hết (ví dụ chỉ cần ID của một dòng trong tập data) thì cách gọi có thể sai vì lúc này cần làm concurrency nhưng bản thân việc async/await trên method láy data là không sai.

Nguồn bài viết:
https://tech.homestudy.edu.vn/thread/thac-mac-ve-asyn-await

https://tech.homestudy.edu.vn 19-05-2019

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Kim Long Dinh

13 bài viết.
2 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
3 0
Đây là một trong loạt bài về kinh nghiệm xin việc, bài trước nói về kinh nghiệm viết resume. Giống như bài trước, bài này cũng chỉ dựa vào những ki...
Kim Long Dinh viết 6 tháng trước
3 0
White
3 0
Một hệ thống cũng giống như thời tiết xấu vậy. Nó không thể đoán trước và cũng không thể tránh khỏi. Và điều quan trọng nhất đối với 1 software eng...
Kim Long Dinh viết 6 tháng trước
3 0
White
1 0
Các loại mesaage queue thường dùng trong Microservices Câu hỏi: Trong khi phát triển microservices system thỉnh thoảng chúng ta hay phải dùng mes...
Kim Long Dinh viết 6 tháng trước
1 0
Bài viết liên quan
White
0 4
fCC: Technical Documentation Page note So I have finished the HTML part of this exercise and I want to come here to lament about the lengthy HTML ...
HungHayHo viết hơn 1 năm trước
0 4
White
2 0
I used Spring boot, Hibernate few times back then at University, I'v started using it again recently. In this (Link), I want to check how Spring J...
Rey viết 10 tháng trước
2 0
White
23 1
Toán tử XOR có tính chất: + A XOR A = 0 + 0 XOR A = A Với tính chất này, có thể cài đặt bài toán sau với độ phức tạp O(N) về runtime, và với O(1)...
kiennt viết 3 năm trước
23 1
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
13 bài viết.
2 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!