Türkiye'de ilk Mobil & PC Aynı Anda Metin2 Oyna. Triarchonline kalıcı ve uzun ömürlü yapısı ile 24 Temmuz'da açılıyor. | 1-99 Mobil Metin2 Triarch HEMEN TIKLA!
[NODE JS] Worker Thread ile CPU Yoğun İşlem
Bir keresinde production'da çalışan bir Node.js API'si vardı ve kullanıcılar büyük CSV dosyalarını yükleyip analiz sonuçlarını istiyordu. İlk versiyonda her şeyi ana thread'de yapıyordum. Küçük dosyalarda sorun yoktu ama 500 bin satırlık bir CSV geldiğinde sunucu 30 saniye boyunca donuyordu. O 30 saniye boyunca hiçbir kullanıcıya yanıt veremiyorduk. İşte tam bu senaryo, worker thread'lerin neden var olduğunu gösteren klasik bir örnektir.
CPU yoğun işlemler, event loop'u bloke eden işlemlerdir. Bunlar genellikle büyük döngüler, karmaşık hesaplamalar, veri dönüştürme ve şifreleme gibi işlemlerdir. Node.js'in asenkron yapısı bu tür işlemlere karşı savunmasızdır çünkü event loop tek bir thread üzerinde çalışır. Bir hesaplama 5 saniye sürüyorsa, o 5 saniye boyunca başka hiçbir iş yapılamaz.
Worker thread kullanarak CPU yoğun işlemleri ana thread'den ayırdığınızda, API'niz yanıt vermeye devam eder. Bu yaklaşımın güzelliği, işlemi tamamen izole etmenizdir. Worker thread kendi bellek alanında çalışır, kendi hata yönetimi vardır ve ana thread'i hiçbir şekilde etkilemez.
Peki hangi işlemler CPU yoğundur? Benim deneyimlerime göre şunlar: büyük dizileri sıralama, Fibonacci veya asal sayı hesaplama gibi matematiksel işlemler, görüntü boyutlandırma ve format dönüştürme, büyük JSON verilerini ayrıştırma, regex ile büyük metinlerde arama yapma, şifreleme ve hash hesaplama. Bu işlemlerin ortak özelliği, I/O beklemesi değil, tamamen CPU hesaplaması yapmalarıdır.
SharedArrayBuffer kullanımı, CPU yoğun işlemlerde performansı artırmanın en etkili yollarından biridir. Normal mesajlaşmada veri kopyalanır, bu da büyük veri setlerinde ciddi bir ek maliyet oluşturur. SharedArrayBuffer ile ana thread ve worker aynı bellek bölgesini kullanır. Ancak bu güç, sorumlulukla birlikte gelir. Paylaşılan belleğe eşzamanlı yazma yarış durumlarına yol açabilir. Atomics API bu sorunu çözer: Atomics.add, Atomics.store, Atomics.load gibi fonksiyonlar thread-safe erişim sağlar.
Bir diğer önemli konu da görev bölümlendirmesidir. Eğer büyük bir veri setini işliyorsanız, veriyi parçalara ayırıp her parçayı farklı bir worker'a vermeniz gerekir. Bu map-reduce benzeri bir yaklaşımdır. Her worker kendi parçasını işler, sonuçlar ana thread'de birleştirilir. Bu strateji ile işlem süresi neredeyse doğrusal olarak azalır.
Worker thread sayısını belirleme konusunda genel kural, CPU çekirdek sayısını aşmamaktır. os.cpus().length ile çekirdek sayısını öğrenebilirsiniz. Genellikle çekirdek sayısının bir eksiğini kullanmak iyi bir pratiktir çünkü bir çekirdeği ana thread'e bırakırsınız. Çekirdek sayısından fazla worker oluşturmak, bağlam değiştirme maliyeti nedeniyle performansı düşürür.
Hata yönetimi de kritik bir konudur. Worker thread'de yakalanmamış bir hata, worker'ı öldürür ama ana thread'i etkilemez. Yine de worker hatalarını yakalayıp uygun şekilde ele almanız gerekir. Belki görevi yeniden denemek, belki kullanıcıya hata mesajı göndermek istersiniz. Her durumda, hataları loglamanız ve izlemeniz önemlidir.
JavaScript:
// ==============================
// CPU Yoğun İşlem: Asal Sayı Hesaplama
// ==============================
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
const os = require('os');
// Asal sayı kontrolü (CPU yoğun)
function asalMi(sayi) {
if (sayi < 2) return false;
if (sayi === 2) return true;
if (sayi % 2 === 0) return false;
for (let i = 3; i <= Math.sqrt(sayi); i += 2) {
if (sayi % i === 0) return false;
}
return true;
}
if (isMainThread) {
// CPU çekirdek sayısını al
const cpuSayisi = os.cpus().length;
console.log('CPU Çekirdek Sayısı:', cpuSayisi);
const ARALIK_BASLANGIC = 2;
const ARALIK_BITIS = 1000000;
const WORKER_SAYISI = Math.min(cpuSayisi - 1, 4);
console.log('Worker sayısı:', WORKER_SAYISI);
console.log(`${ARALIK_BASLANGIC} ile ${ARALIK_BITIS} arasındaki asal sayılar hesaplanıyor...`);
const baslangicZamani = Date.now();
// Aralığı worker'lara böl
const parcaBoyutu = Math.ceil((ARALIK_BITIS - ARALIK_BASLANGIC) / WORKER_SAYISI);
const sonuclar = [];
let tamamlanan = 0;
for (let i = 0; i < WORKER_SAYISI; i++) {
const parcaBaslangic = ARALIK_BASLANGIC + (i * parcaBoyutu);
const parcaBitis = Math.min(parcaBaslangic + parcaBoyutu, ARALIK_BITIS);
const worker = new Worker(__filename, {
workerData: {
baslangic: parcaBaslangic,
bitis: parcaBitis,
workerNumarasi: i + 1
}
});
worker.on('message', (mesaj) => {
if (mesaj.tip === 'ilerleme') {
console.log(`Worker ${mesaj.workerNo}: %${mesaj.yuzde} tamamlandı`);
} else if (mesaj.tip === 'sonuc') {
sonuclar.push(...mesaj.asalSayilar);
tamamlanan++;
if (tamamlanan === WORKER_SAYISI) {
const sure = Date.now() - baslangicZamani;
sonuclar.sort((a, b) => a - b);
console.log(`
Toplam asal sayı: ${sonuclar.length}`);
console.log(`İlk 10: ${sonuclar.slice(0, 10).join(', ')}`);
console.log(`Son 10: ${sonuclar.slice(-10).join(', ')}`);
console.log(`Hesaplama süresi: ${sure} ms`);
}
}
});
worker.on('error', (hata) => {
console.error(`Worker ${i + 1} hatası:`, hata.message);
});
}
} else {
// Worker thread kodu
const { baslangic, bitis, workerNumarasi } = workerData;
const asalSayilar = [];
const toplamSayi = bitis - baslangic;
let islenen = 0;
let sonIlerleme = 0;
for (let sayi = baslangic; sayi < bitis; sayi++) {
if (asalMi(sayi)) {
asalSayilar.push(sayi);
}
islenen++;
const yuzde = Math.floor((islenen / toplamSayi) * 100);
// Her %25'te ilerleme bildir
if (yuzde >= sonIlerleme + 25) {
sonIlerleme = yuzde;
parentPort.postMessage({
tip: 'ilerleme',
workerNo: workerNumarasi,
yuzde: yuzde
});
}
}
parentPort.postMessage({
tip: 'sonuc',
asalSayilar: asalSayilar,
workerNo: workerNumarasi
});
}
// ==============================
// SharedArrayBuffer ile Veri Paylaşımı
// ==============================
if (isMainThread) {
// Paylaşılan bellek alanı oluştur (1 milyon tamsayı)
const sharedBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 1000000);
const sharedArray = new Int32Array(sharedBuffer);
// Diziyi rastgele verilerle doldur
for (let i = 0; i < sharedArray.length; i++) {
sharedArray[i] = Math.floor(Math.random() * 1000);
}
console.log('Paylaşılan dizi oluşturuldu, boyut:', sharedArray.length);
const worker = new Worker(__filename, {
workerData: { sharedBuffer }
});
worker.on('message', (sonuc) => {
console.log('Toplam:', sonuc.toplam);
console.log('Ortalama:', sonuc.ortalama);
console.log('Minimum:', sonuc.minimum);
console.log('Maksimum:', sonuc.maksimum);
});
} else {
const { sharedBuffer } = workerData;
const dizi = new Int32Array(sharedBuffer);
let toplam = 0;
let minimum = Infinity;
let maksimum = -Infinity;
for (let i = 0; i < dizi.length; i++) {
toplam += dizi[i];
if (dizi[i] < minimum) minimum = dizi[i];
if (dizi[i] > maksimum) maksimum = dizi[i];
}
parentPort.postMessage({
toplam,
ortalama: (toplam / dizi.length).toFixed(2),
minimum,
maksimum
});
}
Worker thread'ler ile CPU yoğun işlemleri yönetmek, Node.js uygulamalarınızın performansını dramatik şekilde artırır. Anahtar noktalar şunlardır: veriyi parçalara ayırın, her parçayı ayrı bir worker'a verin, sonuçları ana thread'de birleştirin. SharedArrayBuffer büyük veri setleri için idealdir. Worker sayısını CPU çekirdek sayısıyla sınırlayın ve hata yönetimini ihmal etmeyin.
Emeğiniz ve katkınız için teşekkür ederiz!
Konunuz gerekli incelemelerden geçerek onaylandı. Topluluğumuza katkılarınızın devamını bekliyoruz.
İyi forumlar!
Konunuz gerekli incelemelerden geçerek onaylandı. Topluluğumuza katkılarınızın devamını bekliyoruz.
İyi forumlar!

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)
Benzer konular
- Kilitli
- Cevaplar
- 7
- Görüntüleme
- 2K
- Kilitli
- Cevaplar
- 1
- Görüntüleme
- 439
- Cevaplar
- 8
- Görüntüleme
- 427
- Cevaplar
- 2
- Görüntüleme
- 52
- Cevaplar
- 5
- Görüntüleme
- 506
