@sirhung1993 bạn update #1, kèm các link tới các post khác, cho dễ theo dõi nhé
Mình có update rồi, vừa viết vừa nghĩ nên không cập nhật 1 lúc được@sirhung1993 bạn update #1, kèm các link tới các post khác, cho dễ theo dõi nhé
Phần async/non-blocking IO mình có giải thích ở trên. Là 1 pattern dựa trên trên multi-threading.code .net toàn spam async await không não vào nhìn quả multithread bên java xong trầm cmn cảm
package main
import (
"fmt"
"math/rand"
)
func CalculateValue(values chan int) {
value := rand.Intn(10)
fmt.Println("Calculated Random Value: {}", value)
values <- value
}
func main() {
fmt.Println("Go Channel Tutorial")
values := make(chan int)
defer close(values)
go CalculateValue(values)
value := <-values
fmt.Println(value)
}
@GET @Path("/hello-wolrd")
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello";
}
@Singleton
public class TestClass {
private boolean isOk = false;
private final Map<String, String> str2str = new HashMap<>();
public void setOk(boolean status) {
isOk = status;
}
public boolean getStatus() {
return isOk;
}
public void insertStr(String key, String value) {
str2str.putIfAbsent(key, value);
}
public String getAKey (String key) {
return str2str.get(key);
}
}
private volatile boolean isOk = false;
//hoặc
private AtomicBoolean isOk = new AtomicBoolean(false)
private final Map<String, String> str2str = new ConcurrentHashMap<>();
( nhiều từ cố gắng dùng tiếng việt cơ mà tối nghĩa thực sự )
- go CalculateValue(values) -> trigger go-routine chạy function/lambda ở 1 thread-scope khác
- có đọc sơ qua doc thì go-routine này ko hẳn là 1 thread-OS độc lập
- thread-pool được go-lang tạo sẵn lúc start-up mà share chung cho nhiều go-routine
- go-routine được cho là light-weight hơn cách Java new Thread() -> dễ hiểu là nó chỉ là 1 cấu trúc dữ liệu trên memory -> đẩy vào task-queue -> thread-pool có sẵn
- Phần này mình suy đoán chỗ go-routine là in-memory datastructure , ai có cao kiến khác thì bổ sung ? ( như Java thread thì ko hẳn là in-memory , mà có 1 file_descriptor mô tả nó trên OS)
Go-lang tôi có code qua thôi, đi support cho team khác nên không sâu.Ngoài đề chút mong ông mod cho tâm tư vài lời nhé:
Thực ra hay đọc cái box CNTT này , cơ mà cũng oải. Rất lâu rồi có topic hay như nầy , chất lượng như nầy. Lúc đầu định viết vài dòng về bài toán coroutine vs event loop. Cơ mà đến bây giờ thì tự cảm thấy trình độ của kẻ ngoại đạo thật ko dám so bì với các thím ở đây. Cũng hơi buồn chút các "developer" Vn nói chung chắc ko thèm quan tâm tới mức này. Chẳng biết dùng từ gì cho "hạp" lý , chỉ mong các thím chung tay làm một bài toán cụ thể cho anh sáng tỏ - demo mấy thứ trên thì tốt quá. Ko biết các cụ có hưởng ứng ...
Phần routine trong Go tôi nghĩ các cụ cũng đi chi tiết + có demo chút. Món coroutine này bản chất nó có tới 2 kiểu chính " stackless coroutines stackful coroutines ". Các cụ đã nhắc , nhắc kỹ cho trót cho bà con anh em chúng tôiđược hiểu rõ hơn.
( nhiều từ cố gắng dùng tiếng việt cơ mà tối nghĩa thực sự )
Cũng theo như mình được biết. Go-routine thuộc kiểu "stackful coroutines". Có Stack riêng cho từng thread của nó. Bản chất là "context switch- chuyển đổi ngữ cảnh" ở chế độ người dùng-user mode. Ko cần gọi tới api thread của OS ở chế độ " kernel mode ". Rất nhẹ tốn ít chu kỳ=> độ trễ sẽ thấp so với thread truyền thống của OS.
Tham khảo - https://blog.tsunanet.net/2010/11/how-long-does-it-take-to-make-context.html
Tuy nhiên thèn go này cũng có nhiều vấn đề ở cách tổ chức stack. Nói thì dài dòng các thím có thể đọc thêm ở vụ này.
https://agis.io/post/contiguous-stacks-golang/
http://www.mikemarcin.com/post/coroutine_a_million_stacks/
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006314.html
Mong các thím chỉ giáo thêm về vấn đề này.
Mình mới tìm được cách để chạy call-back ở worker-threadMình thì hiểu đơn giản về việc một ngôn ngữ có support multi-thread hay không như thế này:
Khi mình viết 1 đoạn code
thì có hay không khả năng mộtJavaScript:statement1(); statement2();
statement3
giờ ơi đất hỡi ở đâu đó (tất nhiên là trong cùng 1 chương trình, cùng ngôn ngữ đó) chạy chen vào không.
Với Javascript thì theo mình biết là không thể. Và vì thế lập trình viên cũng không phải lo lắng về concurrent modification trong Js.
Chứ các bác cứ lôi những cái sâu bên dưới như kernel thread, OS hay đến cả pipeline trong CPU ra thì vô cùng lắm. Cái mình muốn nhấn mạnh là chỉ giới hạn trong phạm vi ngôn ngữ đó thôi.
const { Worker, isMainThread, parentPort, threadId } = require('worker_threads');
const arr = [0];
if (isMainThread) {
const worker = new Worker(__filename);
setInterval(() => {
arr[0] = arr[0] + 1;
worker.postMessage(arr[0]);
//console.log(worker.performance.eventLoopUtilization());
console.log(`Worker.isMainThread: ${isMainThread} parent-scope: ${arr[0]} TID: ${threadId} `);
}, 100).unref();
return;
}
parentPort.on('message', (mess) => {
arr[0] = arr[0] + 2;
console.log(`Worker.isMainThread: ${isMainThread} worker-scope: and arr[0]: ${arr[0]} TID: ${threadId}`);
}).unref();
(function r(n) {
if (--n < 0) return;
const t = Date.now();
while (Date.now() - t < 300);
setImmediate(r, n);
})(10);
node -v
v16.13.0
Worker.isMainThread: true parent-scope: 1 TID: 0
Worker.isMainThread: true parent-scope: 2 TID: 0
Worker.isMainThread: true parent-scope: 3 TID: 0
Worker.isMainThread: true parent-scope: 4 TID: 0
Worker.isMainThread: true parent-scope: 5 TID: 0
Worker.isMainThread: true parent-scope: 6 TID: 0
Worker.isMainThread: false worker-scope: and arr[0]: 2 TID: 1
Worker.isMainThread: true parent-scope: 7 TID: 0
Worker.isMainThread: true parent-scope: 8 TID: 0
Worker.isMainThread: true parent-scope: 9 TID: 0
Worker.isMainThread: false worker-scope: and arr[0]: 4 TID: 1
Worker.isMainThread: false worker-scope: and arr[0]: 6 TID: 1
Worker.isMainThread: false worker-scope: and arr[0]: 8 TID: 1
Worker.isMainThread: false worker-scope: and arr[0]: 10 TID: 1
Worker.isMainThread: false worker-scope: and arr[0]: 12 TID: 1
Worker.isMainThread: true parent-scope: 10 TID: 0
Worker.isMainThread: true parent-scope: 11 TID: 0
Worker.isMainThread: true parent-scope: 12 TID: 0
Worker.isMainThread: false worker-scope: and arr[0]: 14 TID: 1
Worker.isMainThread: false worker-scope: and arr[0]: 16 TID: 1
Worker.isMainThread: false worker-scope: and arr[0]: 18 TID: 1
Worker.isMainThread: true parent-scope: 13 TID: 0
Worker.isMainThread: true parent-scope: 14 TID: 0
Worker.isMainThread: true parent-scope: 15 TID: 0
Worker.isMainThread: false worker-scope: and arr[0]: 20 TID: 1
Worker.isMainThread: false worker-scope: and arr[0]: 22 TID: 1
Worker.isMainThread: false worker-scope: and arr[0]: 24 TID: 1
...
OK. Tóm lại khẳng định ban đầu của mình vẫn đúng (ít nhất là trong môi trường NodeJS hiện nay) khi mà các worker thread của NodeJS là các process riêng biệt của hệ điều hành. Và lập trình viên bình thường hoàn toàn không phải lo về concurrent modification (do không có share memory).Mình mới tìm được cách để chạy call-back ở worker-thread
Như kết qủa trên thì mình khá chắc là thread trong NodeJs ko share cùng phân vùng memory.
Có thể thấy là ở parent-scope việc thay đổi gía trị ko ảnh hưởng gì đến worker-thread.
Cách duy nhất để sync là lúc worker xử lý xong và bắn message về parent -> 1 dạng queue
Có vẻ worker-thread được V8-engine bọc trong 1 process khác luôn. ( đây là đoán nhé, ko chắc chắn)
SharedArrayBuffer
. Tuy nhiên có vẻ NodeJS chưa hỗ trợ. Có một implementation khác là Webworker (worker thread cho browser) hỗ trợ cái này rồi nhưng đang bị disabled bởi tất cả các major browsers (lý do???).truớc e dùng thì thấy executorService.shutdownNow() nó cũng không thể interupted thread được. Lý do thread này là ở trong pool, kb bác thấy có đúng k ?4. Vai trò của lambda function vs functional programming trong Async/non-blocking IO và multi-threading
Phần này sẽ đi sâu vào cách implement trong Java làm ví dụ:
Java:final ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.execute(() -> { // Executed in another thread // Thread-scope // Dedicated stack-space (default 1MB on memory for each Thread) }); // If the the task done -> then close (not closed yet) executorService.shutdown(); // Block the current thread -> wait until the task done if (executorService.awaitTermination(waitDuration, WAIT_TIME_UNIT)) { logger.info("onEnable init Ignite service successfully"); // Success } else { // Fail -> the task not finish on-time // Force the the thread to close executorService.shutdownNow(); }
Thread-scope là yếu tố tạo nên sự liên quan giữa lambda function/callback vs async/non-blocking IO.
Giải thích sơ qua lý do tại sao có các ràng buộc trên:
- Các biến primitive datatype ( như int,long,boolean...) được lưu trên stack-memory của thread hiện tại, sẽ không cho phép share qua 1 thread(stack-memory) khác - trừ biến final
- Để truyền vào thread-scope khác, cần phải dùng wrapper-datatype ( Integer, Long, Boolean) hoặc Object được shared qua Heap-space. Lưu ý, về lý thuyết thì share được , nhưng sẽ không đảm bảo thread-safe, và compiler cũng bắt buộc phải là biến final ( trên stack lưu address -> heap-memory, thì phải final cái address này rồi mới truyền vào thread-scope khác, không cho phép override lại )
- Khi truyền biến vào 1 thread-scope khác, cần đảm bảo sẽ không có race-condition (thread-safe object) hoặc chỉ có 1 thread duy nhất access vào nó.
Lambda function hay functional programming đều rất hạn chế mutate global var , best practice thì chỉ dùng pure-function ( không sửa xóa các vùng share).
- Về mặt hardware, khi CPU xử lý machine-code, thì nó fetch data từ register -> L1,L2,L3 ... cache -> memory
- Và mỗi core lại có 1 phân vùng L1 vs L2 riêng, có thể L3 lại chung (đọc thêm về MESI protocol) -> giá trị của 1 memory-address có thể bị dirty-cache (khác nhau giữa core vs core kia)
- với point 1 và 2 ở trên thì nếu compiler cho phép truyền trực tiếp memory-address từ thread này sang thread khác -> việc update memory có thể dẫn đến race-condition hoặc unknow-condition
- Để bắt buộc CPU check dirty-cache trước khi fetch giá trị -> dùng keyword volatile (tương tự như keyword register của C++) cho attribute muốn check -> ảnh hưởng đến performance rất lớn
- Việc đảm bảo chỉ 1 thread duy nhất được tác động vào 1 Object -> forEach() , một vài stream api
- hoặc có thể dùng keyword synchonized cho method -> đảm bảo chỉ có 1 thread duy tại 1 thời điểm có thể gọi vào method đó
Cá nhân mình thì hay dùng các thread-safe object ví dụ: BlockingQueue, ConccurentMap vs Atomic để sync giữa các thread.
Thread-safe object thường dùng cơ chế như spin-lock, semaphore hoặc đặc biệt như class Atomic thì support ở tầng assembly ( có các lệnh để tự sync value - update là dùng volatile + Compare and Swap - CAS )
Lúc gọi shutdownNow() thì pool sẽ lần lượt close hết thread trong pool.truớc e dùng thì thấy executorService.shutdownNow() nó cũng không thể interupted thread được. Lý do thread này là ở trong pool, kb bác thấy có đúng k ?
Các ông cứ tự bi , tôi dân ngoại đạo cũng vọc có làm sao. Thớt đang hay lại dừng rồi , cơ mà toàn các khái niệm cũng quen. Giờ mạnh dạn đề nghị anh em làm cái lib bằng asm vs C or C++ để mô phỏng lại go-routine. Đảm bảo hiểu vấn đề ngay mà ... TÔi đang chờ các anh vụ này.Thớt này quá là hard với 1 thằng dev quèn như tôi
Cứ đâm đầu vào là tự thông. Tôi dân điện tử chuyển sang IT , ai cũng từ dưới đi lên cảThớt này quá là hard với 1 thằng dev quèn như tôi
Chắc nó viết đâu đó trong Go-lang github mà tôi lục mãi mà ko thấy,Các ông cứ tự bi , tôi dân ngoại đạo cũng vọc có làm sao. Thớt đang hay lại dừng rồi , cơ mà toàn các khái niệm cũng quen. Giờ mạnh dạn đề nghị anh em làm cái lib bằng asm vs C or C++ để mô phỏng lại go-routine. Đảm bảo hiểu vấn đề ngay mà ... TÔi đang chờ các anh vụ này.
@sirhung1993 cụ cũng nói gần sạch hết các vấn đề rồi , cụ rảnh ko làm cái trên đê cho vui cửa vui nhà. Có khá nhiều vấn đề hay ho theo tôi biết cũng chưa hẳn có phương án ngon. Cơ mà hình như anh em dev ít quan tâm tới "low entry" nhở ?
Bản chất của coroutine vẫn là callback. Nó cũng chỉ là đẩy work sang 1 thread khác ngoài main thread thông qua khối withContext xong dùng callback gọi lại . Cái coroutine nó đơn giản việc bác viết callback. Thay vì callback lồng nhau oằn tà là vằn thì với coroutine có thể viết dạng tuần tự.sẵn đây có cao nhân nào thông não cho em về co-routine của Kotlin với. Vì sao nó có thể chạy nhiều co-rountine trên main-thread mà ko block main-thread vậy nhỉ
đúng rồi cụ giờ đa số compile trước LLVM rồi mới về machine-code. Cơ mà đã nói đến routine thì đa số phải can thiệp vào compiler để có cách dịch riêng cụ à.Chắc nó viết đâu đó trong Go-lang github mà tôi lục mãi mà ko thấy,
khả năng viết ở phần sâu hơn như phần engine/compiler ...
Giờ đa số compile về LLVM rồi mới về machine-code. Viết trực tiếp ASM thì ít lắm.
Còn về C/C++ example thì tôi nghĩ ví dụ của Java trên tôi viết là phần nào đó cover rồi
Đoạn này theo mình nghĩ cũng có liên quan tới callback cơ mà chính xác hơn. Coroutine nó chính là context switch. Lập lịch hợp tác....Bản chất của coroutine vẫn là callback. Nó cũng chỉ là đẩy work sang 1 thread khác ngoài main thread thông qua khối withContext xong dùng callback gọi lại . Cái coroutine nó đơn giản việc bác viết callback. Thay vì callback lồng nhau oằn tà là vằn thì với coroutine có thể viết dạng tuần tự.