thảo luận Javascript có thực sự là single thread ?

  • Người tạo chủ đề Người tạo chủ đề teeeeeeeee
  • Ngày bắt đầu Ngày bắt đầu
đâu cần đợi đâu, truy vấn là IO mà thằng js/node là non-block, còn java thì multi-thread(vẫn có non-blocking IO), còn lại thì coi db nó xử lý concurrency thế nào.
 
làm như thế nào thì bạn có thể tham khảo tại sao OS (hoặc dễ hình dung hơn là mấy cái VPS giá rẻ) dùng một CPU vẫn chạy được đa tác vụ.

giải thích qua loa thì một chương trình đang chạy nó sẽ break thành các "chunk", nó chạy hết chunk này sẽ chạy chunk khác. tất nhiên việc đổi chunk đang chạy sang chunk khác đồng nghĩa với việc xoá hết cache nạp lại + copy data các kiểu, giảm performance đáng kể, implementation cũng phức tạp, cho nên nhiều ngôn ngữ họ thấy không cần cho nên không làm thôi.

cơ mà không phải là không có, BEAM (elarng/elixir) là một thằng tiêu biểu trong vụ tạo mấy "chunk" này cực ngắn, cho nên các bạn thường nghe nói cái BEAM này latency rất thấp, nhưng hiệu năng lại kém mấy thằng khác là vì thế. trong elixir/erlang thì dù bạn có cho chạy vòng for từ 1 tới 10^100 thì các task khác vẫn chạy bình thường, dù chỉ một CPU.
Mấy cái này như coroutine . Nó ko xoá cache đâu thím ( ko chính xác ). Nó dùng cơ chế context sw. Sao lưu khôi phục các thanh ghi trên cpu. Bên linux nó tuân theo mấy cái tiêu chuẩn ABI GÌ gì đó. Đại loại như đối số hàm sẽ ở rsi rdi ... Rsp rbp ...r11 ->r14 phải khôi phục sau khi sử dụng.
Thành ra nó có tác dụng như mutithread nhưng chỉ trên 1 process. Nó khác multithread thật ở chỗ. Chi phí cpu cho vụ context sw ít hơn nhiều so vs context của os. Cơ mà nó vẫn có nhược điểm là cần chuyển đổi ngữ cảnh. Và có rất nhiều regisster ko được backup. Sẽ hạn chế tính toán phức tạp kha khá.
Nó chỉ phù hợp cho nhũng nhiệm vụ kiểu io nhiều. Chứ còn lại thấy ko hợp lắm.
Vụ js chơi kieeu bài loop event rất hiện đại cơ mà vụ call back thì phát hoảng. Chưa kể thằng đằng sau nó Epoll có mấy cái bug chủ chuối vãi. Có một lão ched tơi tả vụ nầy.... Chém gió tí.
 
Mấy cái này như coroutine . Nó ko xoá cache đâu thím ( ko chính xác ). Nó dùng cơ chế context sw. Sao lưu khôi phục các thanh ghi trên cpu. Bên linux nó tuân theo mấy cái tiêu chuẩn ABI GÌ gì đó. Đại loại như đối số hàm sẽ ở rsi rdi ... Rsp rbp ...r11 ->r14 phải khôi phục sau khi sử dụng.
Thành ra nó có tác dụng như mutithread nhưng chỉ trên 1 process. Nó khác multithread thật ở chỗ. Chi phí cpu cho vụ context sw ít hơn nhiều so vs context của os. Cơ mà nó vẫn có nhược điểm là cần chuyển đổi ngữ cảnh. Và có rất nhiều regisster ko được backup. Sẽ hạn chế tính toán phức tạp kha khá.
Nó chỉ phù hợp cho nhũng nhiệm vụ kiểu io nhiều. Chứ còn lại thấy ko hợp lắm.
Vụ js chơi kieeu bài loop event rất hiện đại cơ mà vụ call back thì phát hoảng. Chưa kể thằng đằng sau nó Epoll có mấy cái bug chủ chuối vãi. Có một lão ched tơi tả vụ nầy.... Chém gió tí.
cache nó mang nhiều nghĩa lắm bạn ơi :v
edit: ờ ý tôi là nhiều kiểu cache, từ cache opcode, l1 l2 l3 cache rồi thì đủ kiểu. mỗi lần context switch sẽ involve một đống thứ.

hồi trước cũng đọc một vài bài báo về vấn đề này cơ mà giờ quên mất rồi, đợi lúc nào tìm lại :v
 
Cái event loop rõ ràng là single thread, nhưng các task async chẳng phải đang chạy ở thread khác sao, nó chỉ hook up lại với cái event loop sau khi đã chạy xong. Thôi có cái video + code demo cho dễ hiểu đây:
Async không phải multthread nhé. Chỉ có web work chạy nền thực sự nhưng web work không được phép truy cập DOM object nên cũng hạn chế rồi
 
Mấy cái này như coroutine . Nó ko xoá cache đâu thím ( ko chính xác ). Nó dùng cơ chế context sw. Sao lưu khôi phục các thanh ghi trên cpu. Bên linux nó tuân theo mấy cái tiêu chuẩn ABI GÌ gì đó. Đại loại như đối số hàm sẽ ở rsi rdi ... Rsp rbp ...r11 ->r14 phải khôi phục sau khi sử dụng.
Thành ra nó có tác dụng như mutithread nhưng chỉ trên 1 process. Nó khác multithread thật ở chỗ. Chi phí cpu cho vụ context sw ít hơn nhiều so vs context của os. Cơ mà nó vẫn có nhược điểm là cần chuyển đổi ngữ cảnh. Và có rất nhiều regisster ko được backup. Sẽ hạn chế tính toán phức tạp kha khá.
Nó chỉ phù hợp cho nhũng nhiệm vụ kiểu io nhiều. Chứ còn lại thấy ko hợp lắm.
Vụ js chơi kieeu bài loop event rất hiện đại cơ mà vụ call back thì phát hoảng. Chưa kể thằng đằng sau nó Epoll có mấy cái bug chủ chuối vãi. Có một lão ched tơi tả vụ nầy.... Chém gió tí.

cache nó mang nhiều nghĩa lắm bạn ơi :v
edit: ờ ý tôi là nhiều kiểu cache, từ cache opcode, l1 l2 l3 cache rồi thì đủ kiểu. mỗi lần context switch sẽ involve một đống thứ.

hồi trước cũng đọc một vài bài báo về vấn đề này cơ mà giờ quên mất rồi, đợi lúc nào tìm lại :v

Context switch ở mức process thì có thể sẽ phải flush cache, tùy trường hợp.

Lý do là vì ứng dụng sử dụng địa chỉ bộ nhớ ảo. Nên hoàn toàn có thể phát sinh trường hợp 2 process khác nhau sử dụng chung địa chỉ ảo nhưng thực chất là trỏ đến địa chỉ vật lý khác nhau, hoặc ngược lại là 2 process dùng địa chỉ ảo khác nhau nhưng lại chung địa chỉ vật lý.

Khi đó nếu như cache được tag bằng địa chỉ ảo thì bắt buộc phải flush khi context switch, nếu không sẽ dẫn đến sai lêch. Điều này là khá phổ biến ở cache L1 vì tag địa chỉ ảo nhanh, không cần phải pagewalk để lấy địa chỉ vật lý. L2 và L3 thì thường tag bằng địa chỉ vật lý vì yêu cầu về latency không quá cao như L1, với lại flush L2 với L3 thì tốn chu kỳ CPU (do kích thước nó lớn) và ảnh hưởng đến hiệu năng nhiều ứng dụng khác.

Có một giải pháp đỡ phải flush L1 là gắn thêm pid vào tag để phân biệt, nhưng cái này cũng tùy CPU.

Còn "context switch" theo kiểu async thì chắc không cần flush, vì vẫn chung một thread, chung không gian bộ nhớ ảo.
 
Có mấy bác nói có ý đúng về việc lấy ví dụ về vụ js là single threaded.

Mấy bác lấy ví dụ sync code kiểu như: white(true), alert()... là chưa chuẩn đâu nhé, cái này thể hiện code được chạy tuần tự thôi, api alert() là hàm đồng bộ, trong ngôn ngữ Java cũng không thiếu hàm kiểu này: .readLine() (không nhớ lắm, chờ nhận một tín hiệu từ bàn phím).

Trong js bạn không thể lấy được một ví dụ mà trong đó có nhiều hơn một "luồng" cùng truy cập vào một biến. Kiểu như một "luồng" thêm dữ liệu vào mảng, "luồng" khác "pop" dữ liệu từ mảng đó ra.

Như bác này có nói, js không được impl để làm việc ở trên với tư tưởng "multi threaded"




Tiện đây cũng có một câu khác, câu này được hỏi để phân cấp "trên" fresher hay không? Thật khó để ngay lập tức phân loại được một ứng viên, nhưng mình nghĩ với câu này là khá ok:

"JS là single threaded. Có 2 api để đăng nhập [1 bằng js chạy trên nodejs (express), 1 bằng Java với spring bot], người dùng sẽ gửi lên user/pass, api service truy vấn tới database để trả ra kết quả đăng nhập thành công hay không?. Database là mysql, truy vấn login luôn mất 10 phút :D. Nếu có 2 người dùng đăng nhập lần lượt đồng thời :D (liền nhau, cách nhau không đáng kể), người đăng nhập thứ 2 sẽ phải đợi bao lâu để có thể đăng nhập?", sau đó sẽ có vài câu tại sao nữa, nên bạn nào lanh lợi thì nên giải thích luôn (nếu bạn chắc chắn đoán được câu hỏi tiếp theo). Phiên phiến thôi, chính xác tới từng "phút" là được rồi.
Cái đó phải tùy vào thằng mariadb nó lock lại bao lâu để chờ read ra chứ, k thì sẽ dính race condition sao
 
Mình đang băn kho
Context switch ở mức process thì có thể sẽ phải flush cache, tùy trường hợp.

Lý do là vì ứng dụng sử dụng địa chỉ bộ nhớ ảo. Nên hoàn toàn có thể phát sinh trường hợp 2 process khác nhau sử dụng chung địa chỉ ảo nhưng thực chất là trỏ đến địa chỉ vật lý khác nhau, hoặc ngược lại là 2 process dùng địa chỉ ảo khác nhau nhưng lại chung địa chỉ vật lý.

Khi đó nếu như cache được tag bằng địa chỉ ảo thì bắt buộc phải flush khi context switch, nếu không sẽ dẫn đến sai lêch. Điều này là khá phổ biến ở cache L1 vì tag địa chỉ ảo nhanh, không cần phải pagewalk để lấy địa chỉ vật lý. L2 và L3 thì thường tag bằng địa chỉ vật lý vì yêu cầu về latency không quá cao như L1, với lại flush L2 với L3 thì tốn chu kỳ CPU (do kích thước nó lớn) và ảnh hưởng đến hiệu năng nhiều ứng dụng khác.

Có một giải pháp đỡ phải flush L1 là gắn thêm pid vào tag để phân biệt, nhưng cái này cũng tùy CPU.

Còn "context switch" theo kiểu async thì chắc không cần flush, vì vẫn chung một thread, chung không gian bộ nhớ ảo.
Mình đang nghic băn khoăn vụ stack mem của coroutine. Đại loại ví dụ hàm A càng ngày càng sử dụng nhiều stack ( tăng kích thước nhớ trên stack ). Dẫn đến một lúc nào đó nó sẽ tràn vào vùng stack của hàm B. Vấn đề đọc mã nhiều lib coroutine thấy nó chỉ lưu trữ- khôi phục rsp vs rbp. Chứ ko hề thực hiện cho tất cả đoạn stack của hàm A.
Đang đọc vấn đề này đang tìm biện pháp xử lý giải quyết. Đang định làm một lib coroutine riêng. Đọc mấy bài trên blog bọn Tây cũng ko nhiều. Tụi nó nói phải làm lại trình dịch ....Nghe sao thấy oải. Ý nói vụ này phải được quy về một tính năng ngôn ngữ. Chứ ko thể dùng lib. Cá nhân dang dùng mấy libco cảm thấy có nhiều chỗ chạy ko hài lòng lắm. Cần tùy chỉnh thêm ....
Thím có cao kiến , xin cho tí ý kiến nào :)
 
Mình đang băn kho

Mình đang nghic băn khoăn vụ stack mem của coroutine. Đại loại ví dụ hàm A càng ngày càng sử dụng nhiều stack ( tăng kích thước nhớ trên stack ). Dẫn đến một lúc nào đó nó sẽ tràn vào vùng stack của hàm B. Vấn đề đọc mã nhiều lib coroutine thấy nó chỉ lưu trữ- khôi phục rsp vs rbp. Chứ ko hề thực hiện cho tất cả đoạn stack của hàm A.
Đang đọc vấn đề này đang tìm biện pháp xử lý giải quyết. Đang định làm một lib coroutine riêng. Đọc mấy bài trên blog bọn Tây cũng ko nhiều. Tụi nó nói phải làm lại trình dịch ....Nghe sao thấy oải. Ý nói vụ này phải được quy về một tính năng ngôn ngữ. Chứ ko thể dùng lib. Cá nhân dang dùng mấy libco cảm thấy có nhiều chỗ chạy ko hài lòng lắm. Cần tùy chỉnh thêm ....
Thím có cao kiến , xin cho tí ý kiến nào :)
Em ko hiểu ý của thím. Nếu A và B cùng 1 thread thì sẽ ko bao h xảy ra trường hợp đó vì rsp giảm dần sau mỗi lần call hàm. Còn nếu 2 hàn nằm ở 2 thrad khác nhau thì cũng ko bao h stack va vào nhau cả vì 2 thread riêng sẽ tạo ra 2 stack mem khác nhau chứ ko phải là 2 frame khác nhau trên cùng 1 stack
 
Hiểu đơn giản single thread chỉ là xử lý đồng thời trên một thread. Còn multi thread là 1 process có nhiều thread, mỗi thread độc lập xử lý.
 
Mình đang băn kho

Mình đang nghic băn khoăn vụ stack mem của coroutine. Đại loại ví dụ hàm A càng ngày càng sử dụng nhiều stack ( tăng kích thước nhớ trên stack ). Dẫn đến một lúc nào đó nó sẽ tràn vào vùng stack của hàm B. Vấn đề đọc mã nhiều lib coroutine thấy nó chỉ lưu trữ- khôi phục rsp vs rbp. Chứ ko hề thực hiện cho tất cả đoạn stack của hàm A.
Đang đọc vấn đề này đang tìm biện pháp xử lý giải quyết. Đang định làm một lib coroutine riêng. Đọc mấy bài trên blog bọn Tây cũng ko nhiều. Tụi nó nói phải làm lại trình dịch ....Nghe sao thấy oải. Ý nói vụ này phải được quy về một tính năng ngôn ngữ. Chứ ko thể dùng lib. Cá nhân dang dùng mấy libco cảm thấy có nhiều chỗ chạy ko hài lòng lắm. Cần tùy chỉnh thêm ....
Thím có cao kiến , xin cho tí ý kiến nào :)

Có 2 loại coroutine, stackful và stackless.
Stackful là mỗi coroutine có một stack riêng, được allocate trên heap. Việc này thì thư viện có thể xử lý được.
Còn stackless là tất cả đều dùng chung một stack giống kiểu như hàm bình thường. Để làm được vậy thì cần có trình dịch hỗ trợ vì chỉ nó mới có thông tin về kích thước của stack frame, địa chỉ các await point. Vậy nên C++20 mới phải ra một tính năng mới để hỗ trợ chứ không dùng thư viện được.

Có bài này khá chi tiết: https://blog.panicsoftware.com/coroutines-introduction/
 
Có 2 loại coroutine, stackful và stackless.
Stackful là mỗi coroutine có một stack riêng, được allocate trên heap. Việc này thì thư viện có thể xử lý được.
Còn stackless là tất cả đều dùng chung một stack giống kiểu như hàm bình thường. Để làm được vậy thì cần có trình dịch hỗ trợ vì chỉ nó mới có thông tin về kích thước của stack frame, địa chỉ các await point. Vậy nên C++20 mới phải ra một tính năng mới để hỗ trợ chứ không dùng thư viện được.

Có bài này khá chi tiết: https://blog.panicsoftware.com/coroutines-introduction/
Hay thím ơi, em mới biết vụ này luôn :love:
 
Có 2 loại coroutine, stackful và stackless.
Stackful là mỗi coroutine có một stack riêng, được allocate trên heap. Việc này thì thư viện có thể xử lý được.
Hỏi bác thêm chút về cơ chế I/O callback của nodejs. Event loop thì dễ hiểu rồi, sau khi app request đến DB thì bỏ vào stack, đi xử lý tiếp cái khác đợi DB response. Nhưng với single thread thì nodejs làm ntn để keep connection với DB, và biết được DB response để lôi cái callback trong stack ra?
 
Có 2 loại coroutine, stackful và stackless.
Stackful là mỗi coroutine có một stack riêng, được allocate trên heap. Việc này thì thư viện có thể xử lý được.
Còn stackless là tất cả đều dùng chung một stack giống kiểu như hàm bình thường. Để làm được vậy thì cần có trình dịch hỗ trợ vì chỉ nó mới có thông tin về kích thước của stack frame, địa chỉ các await point. Vậy nên C++20 mới phải ra một tính năng mới để hỗ trợ chứ không dùng thư viện được.

Có bài này khá chi tiết: https://blog.panicsoftware.com/coroutines-introduction/

Đúng là vậy , chính mình cũng đang mò đoạn đó. Có lẽ đúng chỉ có compile mới có thông tin đầy đủ về stack frame của coroutine.
- Ngoài ra theo thím có biện pháp nào hay hơn coroutine ko ? Thực tế thèn này vẫn bị mất fee cho context sw. Chơi theo bài event loop có thực sự ổn ko nhở ? Event loop nếu ko multi thread được thì cũng ko ngon lắm. CƠ mà multi thì lại chạm một rổ vấn đề giảm per của thằng Epoll ( đang bàn về linux - os khác ko rõ )

Em ko hiểu ý của thím. Nếu A và B cùng 1 thread thì sẽ ko bao h xảy ra trường hợp đó vì rsp giảm dần sau mỗi lần call hàm. Còn nếu 2 hàn nằm ở 2 thrad khác nhau thì cũng ko bao h stack va vào nhau cả vì 2 thread riêng sẽ tạo ra 2 stack mem khác nhau chứ ko phải là 2 frame khác nhau trên cùng 1 stack
À mình thực sự ko biết có hiểu sai hay hiểu đúng nữa. Đại loại 2 hàm A B cơ mà nó run trong coroutine chứ ko phải 2 hàm được call kiểu bình thường thím ạ. VD Stack của các coroutine được cấp 1KB chẳng hạn. Số này được lấy từ stack của thớt trong tông số 1MB chẳng hạn. Vì quá trình hoạt động của hàm A đôi khi Stack của nó bị push lên nhiều. Vượt quá size 1kB của nó , lấn sang stack của thèn B chẳng hạn. Lúc restore lại hàm B , stack bị thay đổi không còn nguyên vẹn như lúc ban đầu save thằng B.

Dĩ nhiên coroutine nó đã save các thanh ghi theo tiêu c huẩn ABI , tuy nhiên có nhiều dữ liệu trong quá trình chạy của hàm được save tạm trên Stack.
Hi vọng thím hiểu ý mình định nói ...Ko biết có đúng ko hay sai. Thím có cao kiến xin cứ chỉ bảo.
 
Sửa lần cuối:
Single hay multi nó tùy thuộc vào runtime-environment chứ chả liên quan gì đến ngôn ngữ, vì vậy nên câu hỏi của chủ thớt trên là sai. Trên trình duyệt thì chỉ cho phép single thread nhưng nodejs phía server có thể chạy multi thread ngon lành.
Còn vụ ông anh trên công ty cũng sai, main thread và worker thread ko dính gì đến nhau cả. Phải nhờ thằng trình duyệt là thằng trung gian cho các thread này giao tiếp với nhau :love:
 
đâu cần đợi đâu, truy vấn là IO mà thằng js/node là non-block, còn java thì multi-thread(vẫn có non-blocking IO), còn lại thì coi db nó xử lý concurrency thế nào.
Bạn nói không sai, nhưng nếu xem đây là câu trả lời cho câu hỏi của mình thì cá nhân mình không thể cho "điểm" cao được.
 
đâu cần đợi đâu, truy vấn là IO mà thằng js/node là non-block, còn java thì multi-thread(vẫn có non-blocking IO), còn lại thì coi db nó xử lý concurrency thế nào.
Sao ko cần đợi?
Nói chung đề thiếu dữ kiện lắm. ko đủ để trả lời đc. Vd ở java đi giờ tôi nói min 10 phút hoặc min 20 min đều đúng vì đề thiếu dữ kiện để ràng buộc

via nextVOZ for iPhone
 
cache nó mang nhiều nghĩa lắm bạn ơi :v
edit: ờ ý tôi là nhiều kiểu cache, từ cache opcode, l1 l2 l3 cache rồi thì đủ kiểu. mỗi lần context switch sẽ involve một đống thứ.

hồi trước cũng đọc một vài bài báo về vấn đề này cơ mà giờ quên mất rồi, đợi lúc nào tìm lại :v
Memory cache L1 L2 L3 nó không tự xoá khi switch context đâu bác. Vì nó là cache của core CPU, không liên quan gì thread hết. Mà mình không nghĩ sẽ có cache nào đó sẽ bị xoá khi context switch, mong bác tìm được tài liệu share lại cho mọi người cùng đọc.
 

Thống kê chủ đề

Ngày tạo
teeeeeeeee,
Người trả lời cuối
AnyaKyle,
Trả lời
199
Lượt xem
30.115
Quay lại
Lên đầu trang