akışlar ve dosyalar. Bunu yapmanın en kolay yolu nedir

Sıkıştırma, birleştirme, önbelleğe alma ve paralel bağlantılar oluşturma yöntemlerine baktıktan sonra şu soruyu düşünmek akıllıca olacaktır: Sayfanın hangi bölümü ana HTML dosyasıyla, hangi bölümü yalnızca harici dosyalarla yüklenmeli? ?

Çeşitli optimizasyon tekniklerinin uygulandığı (aynı zamanda, rastgele bir sayfa yüklemek için gerçek bir hızlanma elde edilen) tek bir sayfa şeklinde bir test ortamı oluşturuldu ve tüm bu tekniklerin sayfa yükleme hızını gerçekten nasıl etkilediği gösterildi. ).

Ayrıca, tüm yönler dikkate alınarak, kademelere göre optimal yük dağılımını belirlemek için teorik hesaplamalar yapılmıştır.

Gerçek durum

Pirinç. 29. WebHiTech.ru sitesinin (değişmemiş) yükleme şeması

İndirme akışı varyasyonunun ana fikri, indirme şemasında minimum sayıda "beyaz boşluk" oluşturmaktı. Olarak Şekil l'de görülebilir. Şekil 29'da gösterildiği gibi, sayfa yükünün yaklaşık %80'i boşta olan bağlantılardır (tabii ki bu grafik tarayıcıda açık olan indirme kanallarının gerçek yükünü yansıtmamaktadır, ancak resim netleştiğinde durum pratikte tam olarak aynı değildir. değiştirmek). Paralel indirmeler, yalnızca darboğaz geçildikten sonra başlar ve sayfa önceden yüklendikten sonra (bu durumda) sona erer - CSS dosyasından sonra.

İndirme hızını optimize etmek için paralel olarak indirilen dosya sayısını (dikey oklar) azaltmamız ve bunları mümkün olduğunca sola "kaydırmamız" (yatay ok) gerekir. "Beyaz boşluğu" azaltmak (aslında indirme kanallarının kapalı kalma süresini azaltmak), teorik olarak paralelleştirme nedeniyle indirme hızını artırmalıdır. Bunun doğru olup olmadığını ve nasıl başarılacağını görelim.

Birinci adım: basit bir sayfa

İlk başta, yalnızca HTML dosyasının gzip sıkıştırmasının kullanıldığı normal bir sayfa alındı. Bu, sayfa yüklemeyi hızlandırmak için yapılabilecek en basit şeydir. Bu optimizasyon, diğer her şeyin karşılaştırıldığı temel olarak alındı. Testler için WebHiTech yarışmasının ana sayfası (http://webhitech.ru/) az sayıda ek resim ile hazırlandı (böylece daha fazla dış nesne vardı ve sayfa boyutu arttı).

Sayfanın en başında (head) ilk zaman ölçülür ve window.onload olayı (yalnızca onun tarafından unutmayın, çünkü yalnızca tüm sayfanın tamamen istemci tarayıcısında olmasını garanti eder) son zamandır, sonra fark hesaplanır. Ancak bu çok basit bir örnek, hadi sonraki adımlara geçelim.

İkinci Adım: Resimleri Yeniden Boyutlandırın

İlk olarak, tüm kaynak görüntüleri küçültüyoruz (uygulanan ana teknikler zaten ikinci bölümde ele alınmıştır). Oldukça komik çıktı: toplam sayfa boyutu %8 azaldı ve indirme hızı %8 arttı (yani orantılı bir hızlanma elde ettik).

Ek olarak, resimlerin küçültülmesiyle, stil sayfası (CSS Tidy aracılığıyla) ve HTML dosyasının kendisi (fazladan boşluklar ve satır sonları kaldırıldı) küçültüldü. Sayfada komut dosyası yoktu, bu nedenle genel yükleme süresi çok fazla değişmedi. Ancak bu son değil ve üçüncü adıma geçiyoruz.

Üçüncü adım: hepsi bir arada

data:URI kullanabilir ve tüm görüntüleri ilgili HTML/CSS dosyalarına gömebilirsiniz, böylece sayfa boyutunu (çoğunlukla stil sayfası daha önce sıkıştırılmamış olduğu için gzip sıkıştırması yoluyla) ancak yükleme süresi boyunca %15 daha azaltabilirsiniz. yalnızca %4 azaldı (önbelleğe alma etkinleştirildiğinde, 304 yanıtlı isteklerin sayısı azaldı). Sayfayı ilk kez yüklerken, iyileştirmeler çok daha kararlı: %20.

Elbette CSS dosyası da HTML'ye dahil edildi, bu nedenle tüm sayfa yüklendiğinde sunucuya yalnızca bir istekte bulunuldu (tüm sayfayı birkaç düzine nesneyle görüntülemek için).

Dördüncü adım: akışı kesin

Orijinal monolitik dosyayı birkaç (5-10) eşit parçaya bölmeyi deneyebilirsiniz, bunlar daha sonra birleştirilir ve doğrudan document.body.innerHTML'ye enjekte edilir. Onlar. ilk HTML dosyasının kendisi çok küçüktür (aslında, yalnızca bir ön yükleyici içerir) ve çok hızlı yüklenir ve bundan sonra, indirme kanalını olabildiğince yoğun kullanan daha birçok benzer dosyanın paralel yüklenmesi başlar.

Bununla birlikte, çalışmalar, XHR isteklerinin ve innerHTML'nin istemci üzerinde birleştirilmesinin ek yükünün, bu tür paralelleştirmeden elde edilen kazançlardan çok daha ağır bastığını göstermiştir. Sonuç olarak, sayfa 2-5 kat daha uzun süre yüklenirken, boyut çok fazla değişmez.

Bazı ek yüklerden kaçınmak için XHR istekleri yerine klasik iframe'leri kullanmayı deneyebilirsiniz. Yardımcı olur, ama çok değil. Sayfa yine de istediğimizden 2-3 kat daha uzun süre yüklenecek.

Ve çerçevelerin kullanımı hakkında biraz: iletilen verilerin boyutunu azaltmak için çoğu zaman sitenin en çok kullanılan kısımları üzerlerinde yapılır. Yukarıda belirtildiği gibi, gecikmelerin çoğu, harici nesnelerin boyutundan değil, sayfadaki çok sayıda harici nesneden kaynaklanmaktadır. Bu nedenle, şu anda bu teknoloji geçen yüzyılın 90'larında olduğu kadar alakalı olmaktan uzak.

Ayrıca, site navigasyonu için bir iframe kullanırken, bu navigasyonu güncelleme sorunu olduğunu da belirtmekte fayda var (örneğin, bazı menü öğelerini aktif olarak vurgulamak istiyorsak). Bu sorunun doğru çözümü, kullanıcının JavaScript'i etkinleştirmesini gerektirir ve teknik bir bakış açısından oldukça önemsiz değildir. Genel olarak, bir site tasarlarken çerçeveler olmadan yapabiliyorsanız, bunları kullanmanıza gerek yoktur.

Beşinci Adım: Algoritmik Önbelleğe Alma

İlk üç adımdaki durumu analiz ettikten sonra, tarayıcının harici dosyaları bir şekilde dönüştürülmesi gereken JSON kodu olarak değil, ayrı nesneler olarak yüklemesine izin verirsek hızlanmanın bir kısmının sağlanabileceğini görüyoruz. Buna ek olarak, önbelleğe almanın bazı yönleri açılır: sonuçta, sayfanın yarısını yüklemek daha hızlıdır ve ikinci yarı için, nesnelerin değişmediğini durum kodlarıyla 304 isteklerle kontrol edin. Bu durumda tüm sayfanın müşteri tarafından ilk kez yüklenmesi biraz daha yavaş olacaktır (doğal olarak, bu konudaki karar kaynağın düzenli kullanıcılarının sayısına bağlı olacaktır).

Sonuç olarak, yükleme süresini %5 daha azaltmak mümkün oldu, son hızlanma (dolu önbellek durumunda) %20'ye ulaşırken sayfa boyutu %21 azaldı. Sayfa boyutunun en fazla %50'sini harici nesnelerin yüklenmesine aktarmak mümkündür, ancak nesnelerin boyutu yaklaşık olarak eşit olmalıdır (farklılık %20'den fazla değildir). Bu durumda, önbelleği tam olan kullanıcılar için sayfa yükleme hızı en yüksek olacaktır. Sayfa boş bir önbelleğe sahip kullanıcılar için optimize edilmişse, en iyi sonuç yalnızca tüm harici dosyalar orijinal HTML'ye dahil edildiğinde elde edilir.

son tablo

Aşağıda, alınan tek bir sayfa için tüm optimizasyon sonuçları verilmiştir. İndirme 100 Kb/sn bağlantıda test edildi, toplam sayısı orijinal nesneler: 23.

Adım numarası

Tanım

Toplam boyut (kb)

İndirme süresi (ms)

1 Normal sayfa. Hiçbir şey sıkıştırılmaz (gzip aracılığıyla yalnızca html sunulur) 63 117
2 HTML/CSS dosyaları ve resimler küçültülür 58 108
3 Tek bir dosya. Veri yoluyla eklenen resimler:URI 49 104
4 HTML dosyası 6 adet veriyi paralel olarak yükler ve bunları istemcide toplar 49 233
4.5 HTML dosyası 4 iframe yükler 49 205
5 Seçenek #3, yalnızca JPEG görüntüleri (yaklaşık olarak aynı boyutta) dosyalara yerleştirilir ve sayfanın başındaki (new Image()).src aracılığıyla yüklenir 49 98

Tablo 5 Çeşitli yollar sayfadaki nesnelerin paralel yüklenmesi

Altıncı Adım: Yük Aşamalarının Dengelenmesi

Peki, aşamaları arasında sayfa yüklemeyi en iyi nasıl dengeleriz? Optimum yüklemeyi sağlayan "altın ortalama" nerede? Veri miktarını azaltmak için tüm ipuçlarını zaten tamamladığımız varsayımıyla başlayalım. Bu her zaman yapılabilir, oldukça basittir (çoğu durumda sunucu yapılandırmasında yalnızca küçük değişiklikler gerekir). Statiğin önbelleğe alma başlıklarıyla zaten sunulduğunu da varsayalım (kaynak dosyası son ziyaretten bu yana fiziksel olarak değişmediyse 304 yanıt döndürmek için).

Sıradaki ne? Diğer eylemler, harici dosyaların yapısına bağlıdır. İçinde çok sayıda (ikiden fazla) dosya bulunan sayfasında, stil dosyalarını ve komut dosyalarını birleştirmeniz gerekir. Sayfa önyüklemesinin hızlanması belirgin olacaktır.

Komut dosyalarının hacmi, sıkıştırmadan sonra bile yeterince büyükse (10 Kb'den fazla), kapatmadan önce bunları dahil etmeye değer. veya genellikle birleştirilmiş olay window.onload'a yükleyin (yedinci bölümün başlangıcı, komut dosyalarının dinamik olarak yüklenmesine ayrılmıştır). Burada aslında indirmenin bir kısmını ikinci aşamadan dördüncü aşamaya aktarıyoruz, yalnızca “görsel” sayfa yüklemesi hızlandırılıyor.

Toplam resim sayısı minimum olmalıdır. Ancak burada, hacmin üçüncü yükleme aşamasına eşit olarak dağıtılması da çok önemlidir. Oldukça sık, 50-100 KB'lik bir görüntü indirmenin tamamlanmasını yavaşlatır, onu 3-4 bileşene bölmek genel süreci hızlandırabilir. Bu nedenle, çok sayıda arka plan görüntüsü kullanırken, bunları paralel olarak yüklenecek 10-20'lik bloklara bölmek daha iyidir.

Yedinci Adım: Önbelleğe Alma Dengeleme

Bununla birlikte, üçüncü aşamada sayfada 10'dan fazla harici nesne varsa (resimler ve çeşitli multimedya dosyaları), paralel akış sayısını artırmak için ek bir ana bilgisayar sunmaya değer. Bu durumda, bir DNS sorgusunun maliyeti, ortalama bağlantı kurma süresini azaltarak kendini amorti edecektir. 20 nesneden sonra 3 ana bilgisayar girilmelidir, vb. Toplamda 4'ten fazla değil (çalışmanın gösterdiği gibi çalışma Grubu Yahoo! 4 ana bilgisayardan sonra, ek yükün azalmadan çok artması olasıdır).

Sayfanın ne kadarının HTML dosyasının kendisine dahil edileceği (CSS, JavaScript veya veri biçimindeki kod: URI) ve harici nesnelerde ne kadarının bırakılacağı sorusu çok basit bir şekilde çözülür. Bu durumda bakiye, kalıcı ve bir kerelik ziyaret sayısının oranına yaklaşık olarak eşittir. Örneğin, kullanıcıların %70'i hafta boyunca siteyi ziyaret ederse, sayfanın yaklaşık %70'i harici nesnelerde ve yalnızca %30'u HTML belgesinde olmalıdır.

Bir sayfanın yalnızca bir kez görülmesi gerektiğinde, her şeyi sayfanın kendisine dahil etmek mantıklıdır. Ancak burada psikolojik yönler devreye giriyor. Ortalama bir kullanıcının sayfasının yüklenmesi 3-4 saniyeden fazla sürüyorsa (bir DNS sorgusu ve sunucuya bağlantı süresi dikkate alınarak), o zaman sayfayı iki bölüme ayırmak gerekecektir: oldukça hızlı bir şekilde görüntülenir ve sayfanın geri kalanı.

Hangi yükleme aşamasının optimize edildiğini ve gerçek kullanıcının ne gördüğünü (temiz bir önbellek ve belki yavaş bir kanalla) anlamak çok önemlidir. Belirli örnekler üzerinde sayfa yükleme sürecinin analizi hakkında daha fazla bilgi sekizinci bölümde açıklanmıştır.

Çözüm

Bu nedenle, normal bir sayfa örneğini kullanarak (zaten oldukça iyi yapılmış, kayda değer), yüklenmesini% 15-20 daha hızlandırdık (ve bu, HTML için gzip sıkıştırmasının kullanımını hesaba katmadan, ki bu durumda toplam hızın yaklaşık %10'unu verir). En önemli yöntemler yukarıda zaten verilmiştir, şimdilik sadece sayfa hızını optimize ederken, her zaman tarayıcının dahili mekanizmalarına güvenmenin ve onları JavaScript'te taklit etmeye çalışmamanın daha iyi olduğu söylenebilir (bu durumda, akışın yapay “dilimlenmesinden” bahsediyoruz). Belki gelecekte, istemci makineler bu tür yöntemlerin işe yaraması için yeterince güçlü (veya JavaScript motorları daha iyi optimize edilmiş) hale gelecektir. Şimdi tek bir seçenek var - algoritmik önbelleğe alma.

Sorunun özü.

Bir noktada, çalışan uygulamanız CPU'yu aktif olarak yüklemeye başlar, test cihazı sizi arar ve düzeltmenizi ister!

Bu durumda programcıların olağan eylemleri nelerdir?

  • Mümkünse yerelleştirmeyi ve ardından sorunu bir an önce çözmeyi istiyorlar.
  • Günlükler, geçiş sayaçları ve benzerlerinin eklenmesi başlar. Günlüğü analiz için yeniden oluşturma ve iade etme gereksinimi ile birlikte her şey test cihazına veya müşteriye verilir. Peki, onu çoğaltabilirsen ve her şey netleşecek.
  • "Her şeyin işe yaradığını" varsayın ve sürüm kontrol sistemindeki değişikliklere dayalı olası nedenleri arayın.


Nasıl daha basit bu durumda yapmak?

bazı veri işleme iş parçacıklarının uyandığı/başladığı ve aktif olarak işini yapmaya başladığı veya bazen bir döngüye takıldığı anlamına gelir. Yükleme sırasında yürütme yığınını öğrendikten sonra, yüksek oran Bu davranışın nedenini anlama olasılığı.

Hata ayıklayıcının altında olmadığımız için nasıl öğrenebilirsin?Şahsen ben yardımcı programı kullanıyorum Süreç araştırmacısı iş parçacığı listesini ve yığınlarını görme fırsatı vermek . Yükleyici gerektirmez.

Gösteri uğruna, başvuruma süreç adıyla başladım " Qocr.Application.Wpf.exe", ki katma numara yapmak sonsuz döngü kodu. Şimdi çekirdeği hata ayıklayıcı olmadan yüklemenin nedenini bulalım. Bunu yapmak için sürecin özelliklerine gidiyorum , Daha ileri:

  1. Sekmeye git İş Parçacığı ve %16 oranında yüklenen 1 akış olduğunu görüyoruz. İşlemci.
  2. Bu konuyu seçin ve tıklayın yığın,"İplik için yığın" penceresi İD".
  3. Pencerede iş parçacığımızın burada oluşturulduğunu görüyoruz Qocr.Application.Wpf.exe!<>C. b__36_1+0x3a ve şu anda arıyor GetDirectories yöntemden InitLanguages().

Yukarıdaki işlemleri resimde oklarla göstereceğim:

Programın kaynak kodunu açıp metoda giderek InitLanguages sahte kodumu görebilirsiniz. Bu bilgiyi, yani duracağı yeri bilerek zaten harekete geçebilirsiniz.

Sonsuz bir döngüye neden olan yığın kodu (yukarıdaki örnekten) (kontrol edilebilir):

Özel void InitLanguages() ( new Thread (() => ( while (true ) ( var dir = Directory .GetDirectories(@"C:\" ); ) ; )).Start(); )

Bir varil bal içinde merhem içinde uçun.

Yukarıdaki yöntemi kullanmaya karar verirseniz bilmeniz gereken iki şey:
  1. Oluşturulan ileti dizileri CLR(kodda oluşturuldu .AĞ uygulamalar) durdurulduktan sonra yürütmeye devam etmeyin. Sonuç olarak, iş parçacığı durur ve program yeniden başlatılıncaya kadar asılı kalır.
  2. Yürütme yığını içermiyorsa kullanışlı bilgi, o zaman bir durup yığını birkaç kez görüntülemeye değer. Bir döngü noktasında tökezleme olasılığı çok yüksektir.
  • Tercüme
  • öğretici

Tercümandan: Bu makale, SFML kitaplığına yönelik resmi kılavuzun bir dizi çevirisinin yedincisidir. Bir önceki yazı bulunabilir Bu yazı dizisi, orijinal dili bilmeyen kişilere bu kütüphaneyi tanıma fırsatı vermeyi amaçlamaktadır. SFML, basit ve platformlar arası bir multimedya kitaplığıdır. SFML, oyunlar ve diğer multimedya uygulamaları geliştirmek için basit bir arayüz sağlar. Orijinal makale bulunabilir. Hadi başlayalım.

Akış nedir?

Çoğunuz bir akışın ne olduğunu zaten biliyorsunuz, ancak bu konuda yeni olanlar için ne olduğunu açıklayacağız.

Bir iş parçacığı, esasen, diğer iş parçacıklarıyla paralel olarak çalışan bir talimat dizisidir. Her program en az bir iş parçacığı oluşturur: main() işlevini çalıştıran ana iş parçacığı. Yalnızca ana iş parçacığını kullanan bir program tek iş parçacıklıdır; bir veya daha fazla iş parçacığı eklerseniz, çok iş parçacıklı hale gelir.

Yani kısaca, threadler aynı anda birden fazla şey yapmanın bir yoludur. Bu, örneğin, görüntüler veya sesler yüklenirken animasyonları görüntülemek ve kullanıcı girişini işlemek için yararlı olabilir. Threadler ağ programlamada da yaygın olarak kullanılmaktadır, verilerin alınmasını beklerken uygulama güncellemeye ve çizmeye devam edecektir.

SFML akışları veya std::thread?

En son sürümünde (2011), C++ Standart Kitaplığı, iş parçacıklarıyla çalışmak için bir dizi sınıf sağlar. SFML yazıldığında, C++11 standardı henüz yazılmamıştı ve iş parçacığı oluşturmanın standart bir yolu yoktu. SFML 2.0 piyasaya sürüldüğünde bunu desteklemeyen birçok derleyici vardı. yeni standart.

Yeni standardı destekleyen ve bir başlık dosyası içeren bir derleyici ile çalışıyorsanız, SFML akış sınıflarını unutun ve bunun yerine standart C++ sınıflarını kullanın. Ancak, bu standardı desteklemeyen bir derleyici ile çalışıyorsanız veya kodunuzu dağıtmayı planlıyorsanız ve tam taşınabilirlik elde etmek istiyorsanız, SFML akış sınıfları iyi bir seçimdir.

SFML ile Konu Oluşturma

Yeterince konuşma, koda bir göz atalım. SFML kullanarak iş parçacığı oluşturmayı mümkün kılan sınıfa sf::Thread adı verilir ve (iş parçacığı oluşturma) eylemde şöyle görünür:

#Dahil etmek #Dahil etmek void func() ( // thread.launch() işlevi çağrıldığında bu işlev çalıştırılır (int i = 0; i< 10; ++i) std::cout << "I"m thread number one" << std::endl; } int main() { // создание потока с функцией func в качестве точки входа sf::Thread thread(&func); // запуск потока thread.launch(); // главные поток продолжает быть запущенным... for (int i = 0; i < 10; ++i) std::cout << "I"m the main thread" << std::endl; return 0; }
Bu kodda, thread.launch() çağrıldıktan sonra main ve func işlevleri paralel olarak yürütülür. Bunun sonucu, her iki işlevden gelen metin çıktısının konsolda karıştırılmasıdır.

Akış giriş noktası, ör. iş parçacığı başladığında yürütülecek işlev sf::Thread yapıcısına iletilmelidir. sf::Thread esnek olmaya ve farklı giriş noktalarını kabul etmeye çalışır: üye olmayan işlevler veya sınıf yöntemleri, bağımsız değişkenleri olan veya olmayan işlevler, işlevler vb. Yukarıdaki örnek, bir üye işlevinin nasıl kullanılacağını gösterir, işte birkaç örnek daha.

  • tek bağımsız değişkenli üye olmayan işlev:

    void func(int x) ( ) sf::Thread thread(&func, 5);

  • sınıf yöntemi:

    SınıfımSınıf (genel: void func() ( ) ); Sınıfım nesnesi; sf::Thread thread(&MyClass::func, &object);

  • functor (işlev nesnesi):

    Struct MyFunctor ( void operatörü()() ( ) ); sf::Thread thread(MyFunctor());

Bir işlev kullanan son örnek, en güçlü olanıdır çünkü her tür işlevi kabul edebilir ve bu nedenle sf::Thread sınıfını doğrudan desteklenmeyen birçok işlev türüyle uyumlu hale getirir. Bu özellik özellikle C++11 veya std::bind lambda ifadeleriyle ilgi çekicidir.

// lambda işleviyle sf::Thread thread(()( std::cout)<< "I am in thread!" << std::endl; });
// ile std::bind void func(std::string, int, double) ( ) sf::Thread thread(std::bind(&func, "hello", 24, 0.5));
Bir sınıf içinde sf::Thread kullanmak istiyorsanız, bunun varsayılan bir kurucuya sahip olmadığını unutmayın. Bu nedenle, başlatma listesindeki sınıf oluşturucunuzda başlatmalısınız:

ClassWithThread Sınıfı ( public: ClassWithThread() : m_thread(&ClassWithThread::f, bu) ( ) private: void f() ( ... ) sf::Thread m_thread; );
Nesne başlatıldıktan sonra gerçekten sf::Thread örneğini başlatmanız gerekiyorsa, onu öbek üzerinde oluşturabilirsiniz.

Bir iş parçacığı başlatmak

sf::Thread örneğini başlattıktan sonra, onu bir çalıştırma işleviyle başlatmanız gerekir.

Sf::İş parçacığı dizisi(&func); thread.launch();
launch, yeni iş parçacığının oluşturucusuna ilettiğiniz işlevi çağırır ve çağıran iş parçacığının hemen çalışmaya devam edebilmesi için hemen çıkar.

Konuları Durdurma

İş parçacığının giriş noktası olan işlev değerini döndürdüğünde, bir iş parçacığı otomatik olarak çıkar. Bir iş parçacığının başka bir iş parçacığından tamamlanmasını beklemek istiyorsanız, bekleme işlevini çağırabilirsiniz.

Sf::İş parçacığı dizisi(&func); // thread.launch()'ı başlat; ... // iş parçacığı sonlandırılana kadar yürütme blokları thread.wait();
wait işlevi ayrıca sf::Thread yıkıcısı tarafından örtük olarak çağrılır, böylece bir iş parçacığı sf::Thread örneği yok edildikten sonra çalışır durumda (ve denetimsiz) kalamaz. Konularınızı yönetirken bunu aklınızda bulundurun (makalenin önceki bölümüne bakın).

Bir iş parçacığının askıya alınması

SFML'de bir iş parçacığını duraklatmanın bir yolunu sağlayan bir işlev yoktur; bir iş parçacığını duraklatmanın tek yolu, onu iş parçacığının kodunun içinden yapmaktır. Başka bir deyişle, yalnızca mevcut ileti dizisini duraklatabilirsiniz. Bunu yapmak için sf::sleep işlevini çağırabilirsiniz:

Void func() ( ... sf::uyku(sf::milisaniye(10)); ... )
sf::sleep bir argüman alır, uyku zamanı. Bu süre, hakkındaki makalede gösterildiği gibi herhangi bir birimde ifade edilebilir.

Bu işlevle herhangi bir diziyi, hatta ana diziyi bile duraklatabileceğinizi unutmayın.

Sf::sleep, bir iş parçacığını askıya almanın en etkili yoludur: bir iş parçacığının askıya alındığı süre boyunca, o (iş parçacığı) neredeyse hiç CPU kaynağı tüketmez. Boş bir while döngüsü gibi beklemeye dayalı bir duraklama, CPU'nun %100'ünü tüketir ve hiçbir şey yapmaz. Ancak, uzaklaştırma süresinin sadece bir ipucu olduğunu unutmayın; duraklamanın gerçek uzunluğu (belirttiğiniz süreden daha fazla veya daha az) işletim sistemine bağlıdır. Bu nedenle, çok doğru zamanlama için bu özelliğe güvenmeyin.

Paylaşılan verileri koruma

Bir programdaki tüm iş parçacıkları bir miktar hafızayı paylaşır, kapsamlarındaki tüm değişkenlere erişimleri vardır. Bu çok kullanışlıdır, ancak aynı zamanda tehlikelidir: bir iş parçacığı paralel olarak başladığı andan itibaren, değişkenler veya işlevler farklı iş parçacıkları tarafından aynı anda kullanılabilir. İşlem iş parçacığı için güvenli değilse, tanımsız davranışa neden olabilir (yani çökebilir veya verileri bozabilir).

Paylaşılan verileri korumanıza ve kodunuzu iş parçacığı için güvenli hale getirmenize yardımcı olabilecek birkaç yazılım aracı vardır; bunlara senkronizasyon temel öğeleri denir. En yaygın ilkeller muteksler, semaforlar, koşul değişkenleri ve spinlock'lardır. Hepsi aynı konseptin varyantlarıdır: sadece bir iş parçacığına verilere erişim hakkı vererek ve gerisini engelleyerek bir kod parçasını korurlar.

En yaygın (ve kullanılan) ilkel mutekstir. Mutex, Karşılıklı Dışlama anlamına gelir. Bu, yalnızca bir iş parçacığının kodu çalıştırabileceğinin garantisidir. Aşağıdaki örnekte mutekslerin nasıl çalıştığını görelim:

#Dahil etmek #Dahil etmek sf::Mutex muteks; void func() ( mutex.lock(); for (int i = 0; i< 10; ++i) std::cout << "I"m thread number one" << std::endl; mutex.unlock(); } int main() { sf::Thread thread(&func); thread.launch(); mutex.lock(); for (int i = 0; i < 10; ++i) std::cout << "I"m the main thread" << std::endl; mutex.unlock(); return 0; }
Bu kod, paylaşılan bir kaynak (std::cout) kullanır ve gördüğümüz gibi, bu istenmeyen sonuçlara yol açar. Akışların çıktısı konsolda karıştırılır. Çıktının doğru yazdırıldığından emin olmak için birbirine karışmak yerine kodun ilgili alanlarını bir muteks ile koruyoruz.

mutex.lock() çağrısına ulaşan ilk iş parçacığı mutex'i kilitler ve metni yazdıran koda erişim sağlar. Diğer evreler mutex.lock() çağrısına ulaştığında, muteks zaten kilitlidir ve diğer evreler yürütmelerini askıya alır (sf::sleep çağrısına benzer şekilde, uyuyan bir evre CPU zamanını tüketmez). İlk iş parçacığı muteksin kilidini açtığında, ikinci iş parçacığı yürütmeye devam eder, muteksi kilitler ve metni yazdırır. Bu, konsoldaki metnin tutarlı bir şekilde yazdırılmasına ve karıştırılmamasına neden olur.

Mutex, paylaşılan verileri korumak için kullanabileceğiniz yalnızca bir ilkel değildir, onu başka şekillerde de kullanabilirsiniz. Ancak, uygulamanız iş parçacığı oluşturma ile zor şeyler yapıyorsa ve mutekslerin gücünün yeterli olmadığını düşünüyorsanız, daha fazla işlevselliğe sahip başka bir kitaplık aramaktan çekinmeyin.

Mutex koruması

Endişelenmeyin: muteksler zaten iş parçacığı için güvenlidir, onları korumaya gerek yoktur. Ama istisna-güvenli değiller. Mutex kilitliyken bir istisna atılırsa ne olur? Asla kilidi açılamaz ve sonsuza kadar kilitli kalacaktır. Kilitli bir muteksin kilidini açmaya çalışan tüm iş parçacıkları sonsuza kadar kilitlenecektir. Bazı durumlarda başvurunuz "dondurulur".

Bir muteksin (muteksin) bir istisna oluşturabileceği bir ortamda her zaman kilidinin açık olduğundan emin olmak için SFML, mutex'i sf::Lock sınıfına sarmanıza izin veren bir RAII sınıfı sağlar. Kilitleme yapıcıda gerçekleşir, kilit açma yıkıcıda gerçekleşir. Basit ve etkili.

Sf::Mutex muteks; void func() ( sf::Lock lock(mutex); // mutex.lock() functionThatMightThrowAnException(); // işlev bir istisna atarsa ​​mutex.unlock()) // mutex.unlock()
sf::Lock öğesinin birden çok dönüş değeri olan işlevlerde de kullanılabileceğini unutmayın.

Sf::Mutex muteks; bool func() ( sf::Lock lock(mutex); // mutex.lock() if (!image1.loadFromFile("...")) false döndürür; // mutex.unlock() if (!image2. loadFromFile("...")) false döndürme; // mutex.unlock() if (!image3.loadFromFile("...")) false döndürme; // mutex.unlock() döndürme true; ) // mutex .Kilidini aç()

Yaygın yanlış anlamalar

Genellikle gözden kaçan bir şey: bir iş parçacığı, karşılık gelen bir örnek olmadan var olamaz

Bu, Windows'taki temel kaynaklar için var olan sınırlamalardan bahsettiğim "Windows'un Sınırlarını Zorlamak" serisinin dördüncü makalesidir. Bu sefer sizinle Windows tarafından desteklenen maksimum iş parçacığı ve işlem sayısı sınırını tartışacağım. Burada, bir iş parçacığı ile bir işlem arasındaki farkı, anket iş parçacığı limitini kısaca açıklayacağım, ardından işlemle ilgili limitlerden bahsedeceğiz. Her aktif işlemin en az bir iş parçacığı (çıkmış, ancak başka bir işlem tarafından sağlanan bir tutamaçta saklanan bir referansı olan bir işlem, bir iş parçacığına sahip olmadığı için) iş parçacıklarının sınırlamalarından bahsetmeye karar verdim. tek iş parçacığı), bu nedenle işlem sınırları doğrudan altta yatan iş parçacığıyla ilgili sınırlara bağlıdır.

UNIX'in bazı türevlerinden farklı olarak, çoğu Windows kaynağının derleme zamanında işletim sisteminde yerleşik sabit bir sınırı yoktur, bunun yerine daha önce tartıştığım işletim sistemi için mevcut olan temel kaynaklara dayalı olarak sınırlıdır. Örneğin işlemler ve iş parçacıkları, fiziksel bellek, sanal bellek ve havuz belleği gerektirir, bu nedenle belirli bir Windows sisteminde oluşturulabilecek işlem ve iş parçacıklarının sayısı, bu işlemlerin nasıl yapıldığına bağlı olarak bu kaynaklardan biri tarafından belirlenir. iş parçacıkları oluşturuldu ve temel alınan kaynak sınırlarından hangisine önce ulaşılacak. Bu nedenle, daha önce yapmadıysanız, önceki makalelerimi okumanızı tavsiye ederim, çünkü daha önceki makalelerimde bahsettiğim ayrılmış bellek, ayrılmış bellek ve sistem belleği sınırı gibi kavramlara ayrıca değineceğim. :

Süreçler ve iş parçacıkları
Bir Windows işlemi, temel olarak, yürütülebilir bir dosyadan komut kodunu depolayan bir kapsayıcıdır. Bu bir çekirdek işlem nesnesidir ve Windows, uygulamanın yürütülebilir koduyla ilgili bilgileri depolamak ve sürdürmek için bu işlem nesnesini ve ilişkili veri yapılarını kullanır. Örneğin, bir işlemin özel ve genel verilerini depolayan ve yürütülebilir görüntüyü ve ilişkili DLL'lerini eşleyen sanal bir adres alanı vardır. Windows, muhasebe ve sorgu yürütme için bir işlemin kaynak kullanımı hakkındaki bilgileri kaydetmek için tanılama araçlarını kullanır ve işlemin işletim sistemi nesnelerine referanslarını işlem tanımlayıcı tablosuna kaydeder. İşlemler, kullanıcı hesabını, hesap gruplarını ve sürece atanan ayrıcalıkları tanımlayan belirteç adı verilen bir güvenlik bağlamıyla çalışır.

Bir işlem, işlemdeki kodu fiilen yürüten (teknik olarak işlemler değil, iş parçacıkları) bir veya daha fazla iş parçacığı içerir ve sistemde çekirdek iş parçacığı nesneleri olarak temsil edilir. Uygulamaların orijinal ilk iş parçacığına ek olarak iş parçacığı oluşturmasının birkaç nedeni vardır: 1) bir kullanıcı arabirimine sahip işlemler, ana iş parçacığını kullanıcı girişiyle ilgili komutlara duyarlı tutarken işlerini yapmak için tipik olarak iş parçacığı oluşturur ve pencere yönetimi; 2) Performansı ölçeklendirmek için birden çok işlemci kullanmak isteyen veya iş parçacıkları boştayken G/Ç'nin senkronize edilmesini beklerken çalışmaya devam etmek isteyen uygulamalar, çoklu iş parçacığından yararlanmak için iş parçacıkları oluşturur.

Konu Sınırları
CPU kayıtlarının durumu, iş parçacığına atanan öncelik ve iş parçacığının kaynak kullanımı hakkındaki bilgiler dahil olmak üzere bir iş parçacığı hakkında temel bilgilere ek olarak, her iş parçacığı, kendisine tahsis edilen işlem adres alanının bir kısmına sahiptir. iş parçacığının program kodunu yürütürken, işlev parametrelerini geçirmek, yerel değişkenleri ve işlev sonuçlarının adreslerini depolamak için çalışan bellek olarak kullanabileceği yığın. Bu nedenle, sistemin sanal belleğini boşa harcamaktan kaçınmak için, başlangıçta yığının yalnızca bir kısmı tahsis edilir veya bir kısmı iş parçacığına aktarılır ve geri kalanı basitçe ayrılır. Bellekteki yığınlar aşağı doğru büyüdüğünden, sistem, gerektiğinde ek belleğin otomatik olarak tahsis edilmesini (yığın genişletmesi olarak adlandırılır) sağlayan yığının tahsis edilen bölümünün dışına belleğin "koruyucu" sayfalarını (İngilizce koruma sayfalarından) yerleştirir. . Aşağıdaki çizim, yığın tahsisinin nasıl derinleştiğini ve yığın 32 bitlik bir adres alanında genişledikçe koruma sayfalarının nasıl hareket ettiğini gösterir:

Yürütülebilir görüntülerin Taşınabilir Yürütülebilir (PE) yapıları, bir iş parçacığı yığını için ayrılan ve başlangıçta ayrılan adres alanı miktarını belirler. Varsayılan olarak, linker 1MB ayırır ve bir sayfa (4K) ayırır, ancak geliştiriciler, programları ile iletişim kurduklarında PE değerlerini değiştirerek veya ayrı bir iş parçacığında CreateTread işlevini çağırarak bu değerleri değiştirebilirler. Yürütülebilir bir programın ayarlarını görüntülemek için Visual Studio ile birlikte gelen Dumpbin gibi bir yardımcı programı kullanabilirsiniz. Yeni Visual Studio projesi tarafından oluşturulan yürütülebilir dosyada Dumpbin'i /headers seçeneğiyle çalıştırmanın sonuçları şunlardır:

Sayıları onaltılıdan çevirerek yığın rezervinin boyutunun 1MB ve ayrılan bellek alanının 4Kb olduğunu görebilirsiniz; Sysinternals'ın MMap adlı yeni bir yardımcı programını kullanarak, bu işleme ekleyebilir ve adres alanına bakabilir ve böylece işlemin başlangıçta tahsis edilen yığın bellek sayfasını, koruma sayfasını ve ayrılmış yığın belleğinin geri kalanını görebilirsiniz:

Her iş parçacığı işlemin adres alanının bir bölümünü tükettiğinden, işlemlerin oluşturabilecekleri iş parçacığı sayısında, adres alanının boyutunun iş parçacığı yığınının boyutuna bölünmesine eşit bir taban sınırı vardır.

32 bit akışların sınırlamaları
İşlemin hiçbir kodu veya verisi olmasa ve tüm adres alanı yığınlar için kullanılabilse bile, varsayılan adres alanı 2 bayt olan 32 bitlik bir işlem maksimum 2048 iş parçacığı oluşturabilir. 32-bit Windows üzerinde -t (iş parçacığı oluştur) seçeneğiyle çalışan Testlimit programının bu sınırlamanın varlığını doğrulayan sonuçları şunlardır:

Bir kez daha, adres alanının bir kısmı kod ve ilk yığın için zaten kullanıldığından, 2 GB'nin tamamı iş parçacığı yığınları için mevcut değildi, bu nedenle oluşturulan toplam iş parçacığı sayısı teorik 2048 iş parçacığı sınırına ulaşamadı.

Uygulamaya genişletilmiş bir adres alanı vermek için ek seçenekle Testlimit'i çalıştırmayı denedim, eğer ona 2GB'tan fazla adres alanı verilmişse (örneğin, 32-bit sistemlerde, bu, uygulamanın /3GB ile çalıştırılmasıyla sağlanır). veya Boot.ini için /USERVA seçeneği veya Vista ve sonraki sürümlerde eşdeğer BCD seçeneği), bunu kullanacaktır. 32-bit işlemlere 64-bit Windows üzerinde çalıştıklarında 4GB adres alanı tahsis edilir, peki 64-bit Windows üzerinde çalışan bir 32-bit Testlimit kaç tane iş parçacığı oluşturabilir? Daha önce tartıştığımız şeye dayanarak, cevap 4096 olmalıdır (4GB bölü 1MB), ancak pratikte bu sayı çok daha düşüktür. 64-bit Windows XP üzerinde çalışan bir 32-bit Testlimit:

Bu tutarsızlığın nedeni, 64-bit Windows'ta 32-bit bir uygulama çalıştırdığınızda, aslında 32-bit iş parçacıkları adına 64-bit kod yürüten ve dolayısıyla bellekte 64-bit bir işlem olmasıdır. her iş parçacığı için alanlar 64-bit ve 32-bit iş parçacığı yığınları için ayrılmıştır. 64 bit yığın için 256 Kb ayrılmıştır (istisnalar, 64 bit iş parçacıklarının ilk yığın boyutunun 1 Mb olduğu Vista öncesi işletim sistemleridir). Her 32 bit iş parçacığı 64 bit modunda başladığından ve başlangıçta ayrılan yığın boyutu sayfa boyutundan daha büyük olduğundan, çoğu durumda 64 bit iş parçacığı yığını için en az 16 Kb ayrıldığını göreceksiniz. 32-bit akışın 64-bit ve 32-bit yığınlarına bir örnek (32-bit yığın "Wow64" olarak etiketlenmiştir):

32-bit Testlimit, 64-bit Windows'ta 3204 iş parçacığı oluşturabildi; bu, her iş parçacığının yığının altında 1Mb + 256Kb adres alanı kullanması gerçeğiyle açıklanır (yine, istisna, 1Mb + 1Mb'nin olduğu Vista'dan önceki Windows sürümleridir). kullanıldı). Ancak 64-bit Windows 7'de 32-bit Testlimit çalıştırarak farklı bir sonuç elde ettim:

Windows XP ve Windows 7'deki sonuçlar arasındaki fark, Windows Vista'nın Adres Alanı Düzeni Rastgeleleştirmesinin (ASLR) daha rastgele yapısından kaynaklanmaktadır ve bu da bazı parçalanmalara neden olmaktadır. DLL yüklemesinin, iş parçacığı yığınının ve dinamik bellek ayırmanın rastgeleleştirilmesi, kötü amaçlı yazılımlara karşı korumanın iyileştirilmesine yardımcı olur. Aşağıdaki VMMap anlık görüntüsünde görebileceğiniz gibi, test sisteminde hala 357 MB kullanılabilir adres alanı var, ancak en büyük boş blok 128 KB'dir ve bu, 32 bitlik bir yığın için gereken 1 MB'den daha azdır:

Belirttiğim gibi, geliştirici varsayılan yığın yedek boyutunu sıfırlayabilir. Bunun olası bir nedeni, iş parçacığı yığınının her zaman varsayılan 1 MB'den daha azını kullanacağı önceden bilindiğinde adres alanını boşa harcamaktan kaçınmak olabilir. Testlimit PE görüntüsü, varsayılan olarak 64K yığın rezervi kullanır ve -t seçeneğiyle -n seçeneğini belirlediğinizde, Testlimit 64K yığınlı evreler oluşturur. Bu yardımcı programı 32-bit Windows XP ve 256MB RAM'e sahip bir sistemde çalıştırmanın sonucu aşağıdadır (bu sınırlamayı vurgulamak için bu testi özellikle zayıf bir sistemde çalıştırdım):

Burada, başka bir hatanın meydana geldiğine dikkat edilmelidir; bu, bu durumda nedenin adres alanı olmadığı anlamına gelir. Aslında, 64Kb yığınların yaklaşık 32.000 iş parçacığı sağlaması gerekir (2Gb/64Kb = 32768). Peki bu durumda sınırlama nedir? Ayrılan bellek ve havuz dahil olası adaylara bakıldığında, bu değerlerin tamamı kendi sınırlarının altında olduğu için bu soruyu cevaplarken herhangi bir ipucu vermiyorlar:

Cevabı, tamamı tükenen kullanılabilir yerleşik bellekle ilgili aradığımız sınırı bize gösterecek olan çekirdek hata ayıklayıcısındaki bellekle ilgili ek bilgilerde bulabiliriz:

Kullanılabilir yerleşik bellek, RAM'de olması gereken veri veya kod için ayrılan fiziksel bellektir. Disk belleği olmayan havuzun ve disk belleği olmayan sürücülerin boyutları ve ayrıca örneğin G/Ç işlemleri için RAM'de ayrılan bellek bağımsız olarak hesaplanır. Her iş parçacığı, daha önce bahsettiğim gibi, her iki kullanıcı modu yığınına sahiptir, ancak aynı zamanda, sistem çağrılarını yürütmek gibi, iş parçacıkları çekirdek modunda çalışırken kullanılan ayrıcalıklı bir mod (çekirdek modu) yığınına da sahiptirler. Bir iş parçacığı etkin olduğunda, çekirdek yığını bellekte sabitlenir, böylece iş parçacığı, gerekli sayfaların eksik olamayacağı çekirdekte kod çalıştırabilir.

Temel çekirdek yığını, 32-bit Windows'ta 12Kb ve 64-bit Windows'ta 24Kb'dir. 14225 iş parçacığı, yaklaşık 170 MB yerleşik bellek gerektirir; bu, Testlimit devre dışı bırakıldığında bu sistemdeki tam olarak boş bellek miktarıdır:

Kullanılabilir sistem belleği sınırına ulaşıldığında, birçok temel işlem başarısız olmaya başlar. Örneğin, masaüstündeki Internet Explorer simgesine çift tıkladığımda aldığım hata:

Beklendiği gibi, 256MB RAM ile 64-bit Windows üzerinde çalışan Testlimit, mevcut bellek tükenmeden önce 6600 thread (bu yardımcı programın 256MB RAM ile 32-bit Windows üzerinde oluşturabildiğinin yarısı kadar) thread oluşturmayı başardı:

Daha önce "temel" çekirdek yığını terimini kullanmamın nedeni, grafik ve pencereleme işlevleriyle çalışan iş parçacığının, ilk çağrıyı yaptığında, 32- başına 20K bayta eşit (veya daha büyük) olan "büyük" bir yığın almasıdır. 64-bit Windows'ta bit Windows ve 48Kb. Testlimit iş parçacıkları bu API'lerin hiçbirini çağırmaz, bu nedenle temel çekirdek yığınlarına sahiptirler.
64 bit akışların sınırlamaları

32 bit iş parçacıkları gibi, 64 bit iş parçacıkları varsayılan olarak 1 Mb yığın rezervine sahiptir, ancak 64 bit iş parçacıkları çok daha fazla kullanıcı adres alanına (8 TB) sahiptir, bu nedenle büyük bir oluşturma söz konusu olduğunda sorun olmamalıdır. iş parçacığı sayısı. Yine de yerleşik kullanılabilir belleğin hala potansiyel bir sınırlayıcı olduğu açıktır. Testlimit'in 64-bit sürümü (Testlimit64.exe), 64-bit Windows XP ve 256MB RAM'e sahip bir sistemde -n seçeneği ile ve seçeneği olmadan yaklaşık 6600 iş parçacığı oluşturabildi, 32-bit sürümle tam olarak aynı sayıda sınıra ulaşıldığından, yerleşik kullanılabilir bellek oluşturuldu. Bununla birlikte, 2 GB RAM'e sahip bir sistemde Testlimit64, yalnızca 55000 iş parçacığı oluşturabildi; bu, yerleşik kullanılabilir bellek sınır olduğunda bu yardımcı programın oluşturabileceği iş parçacığı sayısından önemli ölçüde daha azdır (2 GB/24Kb = 89000):

Bu durumda, neden, sistemin sanal belleğinin tükenmesine ve yetersiz sayfa dosyası alanı nedeniyle bir hata oluşturmasına neden olan, ayrılmış bir başlangıç ​​iş parçacığı yığınıdır. Ayrılan bellek miktarı RAM boyutuna ulaşır ulaşmaz, sistem "atmaya" başladığından, yeni iş parçacığı oluşturma hızı önemli ölçüde azalır, daha önce oluşturulan iş parçacığı yığınları, yeni iş parçacığı yığınlarına yer açmak için değiştirilmeye başlar, ve sayfa dosyası büyümelidir. -n seçeneği etkinleştirildiğinde, ayrılan yığın belleğinin başlangıçtaki miktarı aynı kaldığı için sonuçlar aynıdır.

süreç sınırları
Windows tarafından desteklenen işlem sayısı açıkça iş parçacığı sayısından daha az olmalıdır, çünkü her işlemin bir iş parçacığı vardır ve işlemin kendisi ek kaynak tüketimine neden olur. 64 bit Windows XP ve 2 GB sistem belleğine sahip bir sistemde çalışan 32 bit Testlimit, yaklaşık 8400 işlem oluşturur:

Çekirdek hata ayıklayıcısının sonucuna bakarsanız, bu durumda yerleşik kullanılabilir bellek sınırına ulaşıldığı anlaşılır:

Bir işlem, yalnızca ayrıcalıklı mod iş parçacığı yığınını barındırmak için yerleşik kullanılabilir belleği kullanacak olsaydı, Testlimit 2 GB'lık bir sistemde 8400'den çok iş parçacığı oluşturabilirdi. Testlimit çalışmadan bu sistemdeki kullanılabilir yerleşik bellek miktarı 1,9 GB'dir:

Testlimit'in kullandığı yerleşik bellek miktarını (1,9 GB) oluşturduğu işlem sayısına bölerek işlem başına 230 KB yerleşik bellek elde ediyoruz. 64 bit çekirdek yığını 24 KB olduğundan, her işlem için yaklaşık 206 KB eksik alıyoruz. Kullanılan yerleşik belleğin geri kalanı nerede? Bir işlem oluşturulduğunda, Windows, minimum çalışan bir sayfa kümesi sağlamak için yeterli fiziksel bellek ayırır. Bu, minimum çalışan sayfa kümesini sürdürmek için gereken veri miktarını depolamak için herhangi bir durumda işlemin emrinde yeterli fiziksel belleğe sahip olmasını sağlamak için yapılır. Varsayılan çalışma kümesi boyutu genellikle 200 Kb'dir ve bu, İşlem Gezgini penceresine bir Minimum Çalışma Kümesi sütunu eklenerek kolayca doğrulanabilir:

Kalan 6Kb, işlemin kendisini depolayan ek sayfalanamayan bellek için ayrılan yerleşik kullanılabilir bellektir. 32-bit Windows'taki bir işlem, ayrıcalıklı iş parçacığı yığını daha küçük olduğu için biraz daha az yerleşik bellek kullanır.

Kullanıcı modu iş parçacığı yığınlarında olduğu gibi, işlemler SetProcessWorkingSetSize işleviyle varsayılan çalışma sayfası kümesi boyutunu geçersiz kılabilir. Testlimit, -p seçeneğiyle birlikte ana Testlimit işleminin alt işlemlerini mümkün olan minimum çalışma sayfası boyutu olan 80Kb'ye ayarlayan -n seçeneğini destekler. Alt süreçlerin çalışan sayfa kümelerini azaltmak için zamana ihtiyacı olduğundan, Testlimit artık süreçler oluşturamaz hale geldikten sonra askıya alır ve devam etmeye çalışır, alt süreçlerine çalışma şansı verir. Windows 7 ve 4 GB RAM'e sahip bir sistemde -n seçeneğiyle çalışan Testlimit, yerleşik kullanılabilir bellek sınırından farklı bir sınıra sahiptir - tahsis edilen sistem belleği sınırı:

Aşağıdaki ekran görüntüsünde, çekirdek hata ayıklayıcısının yalnızca sistem bellek sınırına ulaşıldığını değil, sınıra ulaşıldığından beri hem sanal hem de bellekte binlerce bellek ayırma hatası olduğunu bildirdiğini görebilirsiniz. (tahsis edilen sistem belleğinin sınırına aslında birkaç kez ulaşıldı, çünkü sayfa dosyası boyutunun olmamasıyla ilgili bir hata oluştuğunda, aynı miktar artarak bu sınırı zorladı):

Testlimit'in piyasaya sürülmesinden önce, ayrılan ortalama bellek yaklaşık 1,5 GB'dı, bu nedenle iş parçacıkları yaklaşık 8 GB ayrılmış bellek kaplıyordu. Bu nedenle, her işlem yaklaşık 8 GB/6600 veya 1,2 MB tüketmiştir. Her işlem için özel bellek (İngilizce özel bellekten) tahsisini gösteren çekirdek hata ayıklayıcısının !vm komutunu yürütmenin sonucu, bu hesaplamanın doğruluğunu onaylar:

Daha önce açıklanan, iş parçacığı yığını için ayrılan belleğin başlangıçtaki miktarının, işlemin adres alanı veri yapıları, sayfa tablosu girişleri, tanımlayıcı tablosu, işlem ve iş parçacığı nesneleri ve işlemin sırasında oluşturduğu yerel veriler için gereken diğer bellek istekleri üzerinde çok az etkisi vardır. onun başlatılması.

Kaç işlem ve iş parçacığı yeterli olacak?
Yani "Windows kaç tane iş parçacığını destekliyor?" Sorularının cevapları. ve "Windows'ta aynı anda kaç işlem çalıştırabilirsiniz?" birbirine bağlıdır. İş parçacıklarının yığın boyutlarını ve süreçlerin minimum çalışma sayfalarını nasıl belirlediğine ilişkin nüansların yanı sıra, belirli bir sistemde bu soruların yanıtlarını belirleyen iki ana faktör, fiziksel bellek miktarı ve tahsis edilen sistem belleği sınırıdır. Her durumda, bir uygulama bu sınırlara yaklaşmak için yeterli sayıda iş parçacığı veya işlem oluşturursa, geliştiricinin o uygulamayı yeniden tasarlaması gerekir, çünkü makul sayıda işlemle aynı sonuca ulaşmanın her zaman farklı yolları vardır. Örneğin, bir uygulamayı ölçeklendirirken ana hedef, çalışan iş parçacıklarının sayısını CPU sayısına eşit tutmaktır ve bunu başarmanın bir yolu, eşzamanlı G/Ç kullanımından eşzamansız tamamlama bağlantı noktalarının kullanılmasına geçmektir. CPU sayısına göre çalışan iş parçacığı sayısı.