`TypeScript`teki `SharedArrayBuffer`

`TypeScript`teki `SharedArrayBuffer`

Bu makale, TypeScript'teki SharedArrayBuffer'ı açıklar.

Uygulamalı örneklerle TypeScript'te SharedArrayBufferı açıklayacağız.

YouTube Video

TypeScriptteki SharedArrayBuffer

SharedArrayBuffer, aynı bellek alanının birden çok iş parçacığı arasında, örneğin Web Workers gibi, paylaşılması için bir mekanizmadır.. Atomics` ile birlikte kullanıldığında veri yarışmalarını yönetebilir ve düşük gecikmeli paylaşımlı bellek işlemleri yapabilirsiniz.

Önkoşullar ve Notlar

Tarayıcıda SharedArrayBuffer kullanırken, çapraz kaynak izolasyonu olarak bilinen güvenlik gereksinimlerini karşılamak için COOP ve COEP başlıkları gereklidir. Node.js'te, paylaşımlı bellek worker_threads ile nispeten kolay şekilde yönetilebilir.

Temel Kavramlar

SharedArrayBuffer, sabit uzunlukta bayt dizisini temsil eden bir nesnedir ve sayıları TypedArray ve benzeri görünümler üzerinden okuyabilir ve yazabilirsiniz. Basit okuma/yazma işlemleri senkronize değildir, bu nedenle atomik işlemleri garantiye almak ve eşgüdüm için wait ile notify gibi mekanizmaları kullanmak üzere Atomics API'sini kullanırsınız.

Basit Sayaç (Tarayıcı sürümü)

Bu örnekte, ana iş parçacığı bir SharedArrayBuffer oluşturur ve bunu bir Web Workera aktarır; her ikisi de paylaşılan bir sayacı artırır. Bu, en basit kalıbı gösterir: Atomics.add ile atomik toplama ve Atomics.load ile okuma.

main.ts (Tarayıcı tarafı)

Bu örnek, ana iş parçacığının bir SharedArrayBuffer oluşturup onu çoklu iş parçacıklı erişim için bir Worker ile nasıl paylaştığını gösterir.

 1// main.ts
 2// Create a 4-byte (Int32) shared buffer for one counter
 3const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 1);
 4const counter = new Int32Array(sab); // view over the buffer
 5
 6// Create worker and transfer the SharedArrayBuffer
 7const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
 8worker.postMessage({ sab });
 9
10// Increase counter in main thread every 200ms
11setInterval(() => {
12  const newVal = Atomics.add(counter, 0, 1) + 1; // Atomically increment
13  console.log('Main incremented ->', newVal);
14}, 200);
15
16// Listen for messages (optional)
17worker.onmessage = (e) => {
18  console.log('From worker:', e.data);
19};
  • Bu kodda ana iş parçacığı, değeri atomik olarak artırmak için Atomics.add kullanır. worker.ts tarafında aynı SharedArrayBuffera erişilip üzerinde işlem yapılabilir.

worker.ts (Tarayıcı Worker)

Bu, worker'ın aynı paylaşılan tamponu alıp periyodik olarak azaltma veya üzerinde işlem yaptığı bir örnektir.

 1// worker.ts
 2self.onmessage = (ev: MessageEvent) => {
 3  const { sab } = ev.data as { sab: SharedArrayBuffer };
 4  const counter = new Int32Array(sab);
 5
 6  // Every 350ms, atomically add 2 (demonstration)
 7  setInterval(() => {
 8    const newVal = Atomics.add(counter, 0, 2) + 2;
 9    // Optionally notify main thread (postMessage)
10    self.postMessage(`Worker added 2 -> ${newVal}`);
11  }, 350);
12};
  • Worker da aynı belleği Int32Array aracılığıyla işler ve güncellemeler Atomics sayesinde veri yarışması olmadan yapılır.

wait/notify kullanarak senkronizasyon

Atomics.wait ve Atomics.notify kullanarak, belirli koşullar karşılanana kadar thread'leri askıya alabilir ve worker thread'leri içinde olay tabanlı senkronizasyon sağlayabilirsiniz. Tarayıcılarda, en güvenli yöntem Atomics.wait fonksiyonunu bir Worker içinde kullanmaktır.

producer.ts (Tarayıcı Üretici Worker)

Üretici, veriyi yazar ve tüketiciyi notify ile bilgilendirir.

 1// producer.ts (worker)
 2self.onmessage = (ev: MessageEvent) => {
 3  const { sab } = ev.data as { sab: SharedArrayBuffer };
 4  const state = new Int32Array(sab); // state[0] will be 0=empty,1=filled
 5
 6  // produce every 500ms
 7  setInterval(() => {
 8    // produce: set state to 1 (filled)
 9    Atomics.store(state, 0, 1);
10    Atomics.notify(state, 0, 1); // wake one waiter
11    self.postMessage('produced');
12  }, 500);
13};

consumer.ts (Tarayıcı Tüketici Worker)

Tüketici, Atomics.wait ile bekler ve üreticiden bildirim aldığında işlemeye devam eder.

 1// consumer.ts (worker)
 2self.onmessage = (ev: MessageEvent) => {
 3  const { sab } = ev.data as { sab: SharedArrayBuffer };
 4  const state = new Int32Array(sab);
 5
 6  // consumer loop
 7  (async function consumeLoop() {
 8    while (true) {
 9      // if state is 0 => wait until notified (value changes)
10      while (Atomics.load(state, 0) === 0) {
11        // Blocks this worker until notified or timeout
12        Atomics.wait(state, 0, 0);
13      }
14      // consume (reset to 0)
15      // (processing simulated)
16      // read data from another shared buffer in real scenarios
17      Atomics.store(state, 0, 0);
18      // continue loop
19      self.postMessage('consumed');
20    }
21  })();
22};
  • Bu desende, üretici Atomics.notify ile tüketiciyi bilgilendirir ve tüketici de Atomics.wait ile verimli biçimde bekler. Tarayıcılarda ana iş parçacığında Atomics.wait fonksiyonu çağırılamaz. Arayüzün donmasını önlemek için, Atomics.wait ancak Worker içinde kullanılabilir.

Node.js ile Pratik Örnek (worker_threads)

Bir Node.js ortamında, SharedArrayBuffer işlevselliğini worker_threads kullanarak yönetebilirsiniz. Aşağıda Node.js için tipli bir TypeScript örneği bulabilirsiniz.

main-node.ts

Ana iş parçacığı tamponu oluşturur ve Worker'a aktarır.

 1// main-node.ts
 2import { Worker } from 'worker_threads';
 3import path from 'path';
 4
 5// Create SharedArrayBuffer for one 32-bit integer
 6const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
 7const counter = new Int32Array(sab);
 8
 9// Spawn worker and pass sab via workerData
10const worker = new Worker(path.resolve(__dirname, 'worker-node.js'), {
11  workerData: { sab }
12});
13
14// Log messages from worker
15worker.on('message', (msg) => console.log('Worker:', msg));
16
17// Increment in main thread periodically
18setInterval(() => {
19  const val = Atomics.add(counter, 0, 1) + 1;
20  console.log('Main increment ->', val);
21}, 300);

worker-node.ts

Bu örnekte, Worker tarafında worker_threads paketindeki parentPort ve workerData kullanılır.

 1// worker-node.ts
 2import { parentPort, workerData } from 'worker_threads';
 3
 4const sab = workerData.sab as SharedArrayBuffer;
 5const counter = new Int32Array(sab);
 6
 7// Periodically add 5
 8setInterval(() => {
 9  const val = Atomics.add(counter, 0, 5) + 5;
10  parentPort?.postMessage(`Added 5 -> ${val}`);
11}, 700);
  • Node.js'te, tarayıcıdaki gibi COOP veya COEP kısıtlamaları olmadığından, sadece worker_threads kullanarak paylaşımlı bellek kolayca yönetilebilir. TypeScript kullanırken, CommonJS mi yoksa ESM ayarlarıyla mı derlediğinize dikkat edin.

TypeScript Tip Notları

SharedArrayBuffer ve Atomics, standart DOM ve kütüphane tip tanımlarında yer alır, bu yüzden doğrudan TypeScript içerisinde kullanılabilirler. Worker'larla mesaj alışverişinde bulunurken arayüz tanımlamak ve tipleri açıkça belirtmek daha güvenlidir.

1// Example: typed message
2type WorkerMessage = { type: 'init'; sab: SharedArrayBuffer } | { type: 'ping' };
  • Tiplerin açıkça tanımlanması, postMessage ve onmessage işlemlerinin daha güvenli olmasını sağlar ve tip kontrolüne imkan verir.

Pratik Kullanım Senaryoları

SharedArrayBuffer her zaman gerekli değildir, ancak paylaşılan belleğin hız avantajlarının gerektiği durumlarda son derece etkilidir.`. Hangi durumlarda etkili olduğunu anlamak, uygun teknoloji seçimlerine yol açar.

  • Yüksek hızlı paylaşımlı tampon gerektiren ve düşük gecikmeli işlem isteyen durumlarda; gerçek zamanlı ses/görüntü işleme veya oyun fiziği gibi alanlarda kullanılabilir.
  • Basit veri alışverişi ya da büyük miktarda veri aktarımı için, Transferable ArrayBuffer veya postMessage, SharedArrayBufferdan daha kolay olabilir.

Kısıtlamalar ve Güvenlik

Tarayıcıda SharedArrayBuffer kullanmak için çapraz kaynak izolasyonu gerekir: COOP'u same-origin-allow-popups ve COEP'i require-corp olarak ayarlayın. Bu gereksinimler karşılanmazsa, SharedArrayBuffer devre dışı bırakılır.

Performans ve Hata Ayıklama İpuçları

Atomik işlemler (Atomics) hızlıdır, ancak sık bekleme ve aşırı senkronizasyon gecikmeyi artırabilir.

Paylaşımlı belleği güvenli ve verimli şekilde kullanmak için aşağıdaki noktaları kontrol edin.

  • Int32Array gibi görünümler, uygun bayt hizalaması ile kullanılmalıdır.
  • Aynı paylaşımlı tamponun hangi dizinlerinin hangi işlemler tarafından kullanıldığını netleştirin ve kodunuzda tutarlı kullanım kuralları oluşturun.
  • SharedArrayBufferı normal bir ArrayBuffer olarak kullanırsanız, veri yarışmaları meydana gelir. Atomics kullanılmadan, birden fazla iş parçacığının aynı dizine yazması tehlikelidir.

Özet

SharedArrayBuffer ve Atomicsi doğru şekilde kullanarak, TypeScript'te bile güvenli ve hızlı paylaşımlı bellek işlemleri gerçekleştirebilirsiniz. Sayaç veya sinyal gibi basit senkronizasyon kalıplarıyla başlamak daha kolaydır; doğru senkronizasyon ve dizinleri titizlikle yöneterek düşük gecikme gerektiren senaryolarda bile etkili olabilirsiniz.

Yukarıdaki makaleyi, YouTube kanalımızda Visual Studio Code'u kullanarak takip edebilirsiniz. Lütfen YouTube kanalını da kontrol edin.

YouTube Video