PowerShell Çoklu İş Parçacığı: Derinlemesine Bir İnceleme
Bir noktada çoğu insan, temel bir PowerShell betiğinin çözemeyeceği kadar yavaş olduğu bir sorunla karşılaşacaktır. Bu, ağınızdaki birçok bilgisayardan veri toplamak veya Active Directory'de aynı anda tonlarca yeni kullanıcı oluşturmak olabilir. Bunların her ikisi de, daha fazla işlem gücü kullanmanın kodunuzun daha hızlı çalışmasını sağlayacağına dair harika örneklerdir. PowerShell çoklu iş parçacığını kullanarak bunu nasıl çözeceğimize bakalım!
Varsayılan PowerShell oturumu tek iş parçacıklıdır. Bir komutu çalıştırır ve bittiğinde bir sonraki komuta geçer. Bu, her şeyin tekrarlanabilir olmasını sağladığı ve çok fazla kaynak kullanmadığı için güzel. Peki ya gerçekleştirdiği eylemler birbirine bağlı değilse ve ayıracak CPU kaynaklarınız varsa? Bu durumda, çoklu iş parçacıklarını düşünmeye başlamanın zamanı geldi.
Bu makalede, birden fazla veri akışını aynı anda işlemek ancak aynı konsol üzerinden yönetmek için çeşitli PowerShell çoklu iş parçacığı tekniklerini nasıl anlayacağınızı ve kullanacağınızı öğreneceksiniz.
PowerShell Çoklu İş Parçacığını Anlamak
Çoklu iş parçacığı aynı anda birden fazla komutu çalıştırmanın bir yoludur. PowerShell normalde tek bir iş parçacığı kullandığında, kodunuzu paralelleştirmek için birden fazla iş parçacığı kullanmanın birçok yolu vardır.
Çoklu iş parçacığının birincil faydası kodun çalışma süresini azaltmaktır. Bu sürenin azalması, daha yüksek işlem gücü gereksiniminden kaynaklanmaktadır. Çoklu iş parçacığı kullanıldığında, birçok eylem aynı anda gerçekleştirilir ve bu nedenle daha fazla sistem kaynağı gerekir.
Örneğin, Active Directory'de yeni bir kullanıcı oluşturmak isterseniz ne olur? Bu örnekte, yalnızca bir komut çalıştırıldığı için çoklu iş parçacığına yönelik hiçbir şey yoktur. 1000 yeni kullanıcı oluşturmak istediğinizde bunların hepsi değişir.
Çoklu iş parçacığı olmadan, tüm kullanıcıları oluşturmak için Yeni-ADUser komutunu 1000 kez çalıştırırsınız. Belki yeni bir kullanıcı oluşturmak üç saniye sürer. 1000 kullanıcının tamamını oluşturmak bir saatten az sürer. 1000 komut için bir iş parçacığı kullanmak yerine, her biri on komut çalıştıran 100 iş parçacığı kullanabilirsiniz. Artık yaklaşık 50 dakikanızı almak yerine bir dakikanın altına indiniz!
Mükemmel ölçeklendirme göremeyeceğinizi unutmayın. Koddaki öğeleri döndürme ve parçalama eylemi biraz zaman alacaktır. Tek bir iş parçacığı kullanarak PowerShell'in kodu çalıştırması gerekir ve işlem tamamdır. Birden fazla iş parçacığında, konsolunuzu çalıştırmak için kullanılan orijinal iş parçacığı diğer iş parçacıklarını yönetmek için kullanılacaktır. Belirli bir noktada, diğer tüm ileti dizilerini aynı hizada tutarak orijinal ileti dizisinin maksimum sınırına ulaşılır.
PowerShell Çoklu İş Parçacığı İçin Önkoşullar
Bu makalede PowerShell çoklu iş parçacığının nasıl çalıştığını uygulamalı olarak öğreneceksiniz. Devam etmek isterseniz aşağıda ihtiyaç duyacağınız birkaç şey ve kullanılan ortamla ilgili bazı ayrıntılar verilmiştir.
- Windows PowerShell sürüm 3 veya üzeri - Açıkça belirtilmediği sürece her şey, gösterilen tüm kodlar Windows PowerShell sürüm 3 veya üzeri sürümlerde çalışacaktır. Örnekler için Windows PowerShell sürüm 5.1 kullanılacaktır.
- Yedek CPU ve bellek - PowerShell ile paralelleştirmek için en azından biraz fazladan CPU ve belleğe ihtiyacınız olacak. Eğer bu özelliğe sahip değilseniz herhangi bir performans avantajı göremeyebilirsiniz.
Öncelik #1: Kodunuzu Düzeltin!
PowerShell çoklu iş parçacığıyla komut dosyalarınızı hızlandırmaya başlamadan önce tamamlamak isteyeceğiniz birkaç hazırlık çalışması var. Birincisi kodunuzu optimize etmektir.
Daha hızlı çalışmasını sağlamak için kodunuza daha fazla kaynak ayırabilirsiniz ancak çoklu iş parçacığı, çok fazla ekstra karmaşıklık getirir. Çoklu iş parçacığından önce kodunuzu hızlandırmanın yolları varsa, önce bunlar yapılmalıdır.
Darboğazları Belirleyin
Kodunuzu paralelleştirmenin ilk adımlarından biri onu neyin yavaşlattığını bulmaktır. Çoklu iş parçacığı oluşturmadan önce daha hızlı yürütmeye izin vermek için bazı değişiklikler yapabileceğiniz hatalı mantık veya ekstra döngüler nedeniyle kod yavaş olabilir.
Kodunuzu hızlandırmanın yaygın bir yoluna örnek olarak filtrelemenizi sola kaydırabilirsiniz. Bir grup veriyle etkileşim halindeyseniz, veri miktarını azaltmak için izin vermek istediğiniz herhangi bir filtreleme mümkün olduğu kadar erken yapılmalıdır. Aşağıda svchost işlemi tarafından kullanılan CPU miktarını elde etmeye yönelik bazı kod örnekleri verilmiştir.
Aşağıdaki örnek, çalışan tüm süreçlerin okunması ve ardından tek bir işlemin (svchost) filtrelenmesidir. Daha sonra CPU özelliği seçiliyor ve değerin boş olmaması sağlanıyor.
PS51> Get-Process | Where-Object {$_.ProcessName -eq 'svchost'} |
Select-Object CPU | Where-Object {$_.CPU -ne $null}Yukarıdaki kodu aşağıdaki örnekle karşılaştırın. Aşağıda aynı çıktıya sahip ancak farklı düzenlenmiş başka bir kod örneği verilmiştir. Aşağıdaki kodun daha basit olduğuna dikkat edin ve mümkün olan tüm mantığı boru sembolünün soluna kaydırın. Bu, Get-Process'in umursamadığınız işlemleri döndürmesini engeller.
PS51> Get-Process -Name 'svchost' | Where-Object {$_.CPU -ne $null} |
Select-Object CPU
Aşağıda iki hattın yukarıdan çalıştırılması için zaman farkı verilmiştir. Bu kodu yalnızca bir kez çalıştırdığınızda 117 ms'lik fark fark edilmeyecek olsa da, binlerce kez çalıştırdığınızda fark artmaya başlayacaktır.
İş Parçacığı Güvenli Kodu Kullanma
Daha sonra kodunuzun "iş parçacığı açısından güvenli" olduğundan emin olun. "İş parçacığı açısından güvenli" terimi, bir iş parçacığının kod çalıştırıp çalıştırmadığını, başka bir iş parçacığının aynı anda aynı kodu çalıştırıp çakışmaya neden olamayacağını ifade eder.
Örneğin, aynı dosyaya iki farklı iş parçacığında yazmak, önce dosyaya ne ekleyeceğini bilemeyeceği için iş parçacığı açısından güvenli değildir. Oysa bir dosyadan iki iş parçacığının okunması, dosya değiştirilmediği için iş parçacığı açısından güvenlidir. Her iki thread de aynı çıktıyı alır.
İş parçacığı açısından güvenli olmayan PowerShell çoklu iş parçacığı koduyla ilgili sorun, tutarsız sonuçlar alabilmenizdir. Bazen iş parçacıklarının bir çatışmaya neden olmayacak şekilde doğru zamanda gerçekleşmesi nedeniyle iyi çalışabilir. Diğer zamanlarda bir çatışma yaşarsınız ve tutarsız hatalar nedeniyle sorunun giderilmesini zorlaştırır.
Aynı anda yalnızca iki veya üç işi çalıştırıyorsanız, hepsi farklı zamanlarda dosyaya yazdıkları yerde sıralanabilirler. Daha sonra kodu 20 veya 30 işe kadar ölçeklendirdiğinizde, işlerden en az ikisinin aynı anda yazmaya çalışmama olasılığı çok azalır.
PSJobs ile Paralel Yürütme
Bir betiği çoklu iş parçacığı olarak çalıştırmanın en kolay yollarından biri PSJobs'tur. PSJob'larda Microsoft.PowerShell.Core modülünde yerleşik cmdlet'ler bulunur. Microsoft.PowerShell.Core modülü, PowerShell'in sürüm 3'ten itibaren tüm sürümlerinde bulunmaktadır. Bu modüldeki komutlar, ön planda farklı kod çalıştırmaya devam ederken arka planda kod çalıştırmanıza olanak tanır. Mevcut komutların tümünü aşağıda görebilirsiniz.
PS51> Get-Command *-Jobİşlerinizi Takip Etmek
Tüm PSJob'lar on bir eyaletten birindedir. Bu durumlar PowerShell'in işleri yönetme şeklidir.
Aşağıda bir işin bulunabileceği en yaygın durumların bir listesini bulacaksınız.
- Tamamlandı - İş tamamlandı ve çıktı verileri alınabilir veya iş kaldırılabilir.
- Çalışıyor - İş şu anda çalışıyor ve işi durdurmaya zorlamadan kaldırılamaz. Çıktı da henüz alınamıyor.
- Engellendi - İş hala çalışıyor ancak devam etmeden önce ana makineden bilgi alması isteniyor.
- Başarısız - İşin yürütülmesi sırasında bir sonlandırma hatası oluştu.
Başlatılan bir işin durumunu almak için İş Al komutunu kullanırsınız. Bu komut işlerinizin tüm niteliklerini alır.
Aşağıda, durumun Tamamlandı olduğunu görebileceğiniz bir işin çıktısı verilmiştir. Aşağıdaki örnek, Başlat komutunu kullanarak bir iş içinde komutu. Bu işin durumu daha sonra Başlat-Uyku 5 kodunun çalıştırılmasıdır. -İşGet-Job komutu kullanılarak döndürülür.
PS51> Start-Job -Scriptblock {Start-Sleep 5}
PS51> Get-Jobİş durumu Tamamlandı değerini döndürdüğünde, bu, komut dosyası bloğundaki kodun çalıştırıldığı ve yürütülmesinin tamamlandığı anlamına gelir. Ayrıca HasMoreData özelliğinin False olduğunu da görebilirsiniz. Bu, iş bittikten sonra sağlanacak çıktının olmadığı anlamına gelir.
Aşağıda işleri tanımlamak için kullanılan diğer bazı durumların bir örneği bulunmaktadır. Komut sütunundan, bu işlerden bazılarının tamamlanmamasına, örneğin abc saniye boyunca uyumaya çalışmanın sonuç vermesine neden olabileceğini görebilirsiniz. başarısız bir iş.
Yeni İş Yaratmak
Yukarıda gördüğünüz gibi Start-Job komutu, işte kod çalıştırmaya başlayan yeni bir iş oluşturmanıza olanak tanır. Bir iş oluşturduğunuzda, iş için kullanılan bir komut dosyası bloğu sağlarsınız. PSJob daha sonra benzersiz bir kimlik numarasına sahip bir iş oluşturur ve işi çalıştırmaya başlar.
Buradaki temel fayda, Start-Job komutunu çalıştırmanın, kullandığımız komut dosyası bloğunu çalıştırmaya göre daha az zaman almasıdır. Aşağıdaki resimde, komutun tamamlanmasının beş saniye sürmesi yerine, işin başlatılmasının yalnızca 0,15 saniye sürdüğünü görebilirsiniz.
Aynı kodu çok kısa bir sürede çalıştırabilmesinin nedeni, arka planda PSJob olarak çalışıyor olmasıydı. Kodu ön planda çalıştırmak ve aslında beş saniye boyunca uyumak yerine, kurulumu yapıp arka planda çalıştırmaya başlamak 0,15 saniye sürdü.
İş Çıktısını Alma
Bazen işin içindeki kod çıktı döndürür. Receive-Job komutunu kullanarak bu kodun çıktısını alabilirsiniz. Receive-Job komutu, giriş olarak bir PSJob'u kabul eder ve ardından işin çıktısını konsola yazar. Çalışırken işin çıktısı olan her şey depolanmıştır, böylece iş geri alındığında o anda depolananların tümü çıktı olarak alınır.
Bunun bir örneği aşağıdaki kodu çalıştırmak olabilir. Bu, çıktıya Merhaba Dünya yazacak bir iş yaratacak ve başlatacaktır. Daha sonra işten çıktıyı alır ve konsola gönderir.
$Job = Start-Job -ScriptBlock {Write-Output 'Hello World'}
Receive-Job $JobZamanlanmış İşler Oluşturma
PSJob'larla etkileşim kurmanın başka bir yolu da planlanmış bir iş aracılığıyladır. Zamanlanmış işler, Görev Zamanlayıcı ile yapılandırılabilen Windows zamanlanmış görevine benzer. Zamanlanmış işler, karmaşık PowerShell komut dosyası bloklarını zamanlanmış bir görevde kolayca zamanlamanın bir yolunu oluşturur. Zamanlanmış bir işi kullanarak, tetikleyicilere dayalı olarak arka planda bir PSJob çalıştırabilirsiniz.
İş Tetikleyicileri
İş tetikleyicileri belirli bir zaman, bir kullanıcının oturum açması, sistemin önyüklenmesi ve daha birçok şey olabilir. Ayrıca tetikleyicilerin aralıklarla tekrarlanmasını da sağlayabilirsiniz. Bu tetikleyicilerin tümü New-JobTrigger komutuyla tanımlanır. Bu komut, zamanlanmış işi çalıştıracak tetikleyiciyi belirtmek için kullanılır. Tetikleyicisi olmayan zamanlanmış bir işin manuel olarak çalıştırılması gerekir, ancak her işin birçok tetikleyicisi olabilir.
Bir tetikleyiciye sahip olmanın yanı sıra, normal bir PSJob'da kullanılana benzer bir komut dosyası bloğunuz da olacaktır. Hem tetikleyici hem de komut dosyası bloğuna sahip olduğunuzda, işi bir sonraki bölümde gösterildiği gibi oluşturmak için Register-ScheduledJob komutunu kullanırsınız. Bu komut, çalıştırılacak komut dosyası bloğu ve New-JobTrigger komutuyla oluşturulan tetikleyiciler gibi zamanlanmış işin niteliklerini belirtmek için kullanılır.
Demo
Belki birisi bilgisayarda her oturum açtığında çalıştırmak için bazı PowerShell kodlarına ihtiyacınız vardır. Bunun için zamanlanmış bir iş oluşturabilirsiniz.
Bunu yapmak için öncelikle New-JobTrigger kullanarak bir tetikleyici tanımlayacak ve zamanlanan işi aşağıda gösterildiği gibi tanımlayacaksınız. Bu zamanlanmış iş, birisi her oturum açtığında günlük dosyasına bir satır yazacaktır.
$Trigger = New-JobTrigger -AtLogon
$Script = {"User $env:USERNAME logged in at $(Get-Date -Format 'y-M-d H:mm:ss')" | Out-File -FilePath C:\Temp\Login.log -Append}
Register-ScheduledJob -Name Log_Login -ScriptBlock $Script -Trigger $TriggerYukarıdaki komutları çalıştırdığınızda, aşağıda gösterildiği gibi iş kimliğini, komut dosyası bloğunu ve diğer bazı nitelikleri gösterecek yeni bir iş oluştururkenkine benzer bir çıktı alacaksınız.
Birkaç giriş denemesinden sonra, aşağıdaki ekran görüntüsünden girişimlerin günlüğe kaydedildiğini görebilirsiniz.
AsJob Parametresinden Yararlanma
İşleri kullanmanın başka bir yolu da birçok PowerShell komutunda yerleşik olan AsJob parametresini kullanmaktır. Pek çok farklı komut olduğundan bunların hepsini aşağıda gösterildiği gibi Get-Command kullanarak bulabilirsiniz.
PS51> Get-Command -ParameterName AsJobEn yaygın komutlardan biri Invoke-Command'dır. Normalde, bu komutu çalıştırdığınızda, hemen bir komutu yürütmeye başlayacaktır. Bazı komutlar hemen geri dönerek yaptığınız işe devam etmenizi sağlarken, bazıları komut bitene kadar bekleyecektir.
AsJob parametresinin kullanılması, tam olarak neye benzediğini yapar ve yürütülen komutu konsolda eşzamanlı olarak çalıştırmak yerine bir iş olarak çalıştırır.
Çoğu zaman AsJob yerel makinede kullanılabilirken, Invoke-Command'ın yerel makinede çalıştırılacak yerel bir seçeneği yoktur. BilgisayarAdı parametre değeri olarak Localhost'u kullanmanın geçici bir çözümü vardır. Aşağıda bu geçici çözümün bir örneği verilmiştir.
PS51> Invoke-Command -ScriptBlock {Start-Sleep 5} -ComputerName localhostAsJob parametresini çalışırken göstermek için aşağıdaki örnekte, beş saniye boyunca uyumak üzere Invoke-Command kullanılır ve ardından aynı komutu AsJob kullanılarak tekrarlanır. yürütme sürelerindeki farkı gösterir.
PS51> Measure-Command {Invoke-Command -ScriptBlock {Start-Sleep 5}}
PS51> Measure-Command {Invoke-Command -ScriptBlock {Start-Sleep 5} -AsJob -ComputerName localhost}Runspaces: İşlere Benzer Ama Daha Hızlı!
Şu ana kadar PowerShell'de yalnızca yerleşik komutları kullanarak ek iş parçacıklarını kullanmanın yollarını öğreniyordunuz. Komut dosyanızı çoklu iş parçacıklı hale getirmek için başka bir seçenek de ayrı bir çalışma alanı kullanmaktır.
Çalışma alanları, PowerShell'i çalıştıran iş parçacıklarının içinde çalıştığı kapalı alandır. PowerShell konsoluyla kullanılan çalışma alanı tek bir iş parçacığıyla sınırlı olsa da ek iş parçacıklarının kullanımına izin vermek için ek çalışma alanları kullanabilirsiniz.
Runspace ve PSJobs
Bir çalışma alanı ve PSJob pek çok benzerliğe sahip olsa da performans açısından bazı büyük farklılıklar vardır. Çalışma alanları ve PSjobs arasındaki en büyük fark, her birinin kurulması ve parçalanmasının gereken süredir.
Önceki bölümdeki örnekte, oluşturulan PSjob'un açılması yaklaşık 150 ms sürdü. Bu, işin komut dosyası bloğunun çok fazla kod içermemesi ve işe aktarılan hiçbir ek değişkenin olmaması nedeniyle en iyi durumla ilgilidir.
PSJob oluşturmanın aksine, önceden bir çalışma alanı oluşturulur. Bir çalışma alanı işini başlatmak için gereken sürenin büyük bir kısmı herhangi bir kod eklenmeden önce gerçekleştirilir.
Aşağıda PSjob için kullandığımız komutun aynısını çalışma alanında çalıştırmanın bir örneği verilmiştir.
Buna karşılık, runspace sürümü için kullanılan kod aşağıdadır. Aynı görevi yürütmek için çok daha fazla kod olduğunu görebilirsiniz. Ancak ekstra kodun avantajı, komutun 36 ms'ye karşı 148 ms'de çalışmaya başlamasına izin vererek zamanın neredeyse 3/4'ünü kısaltır.
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.Runspace = $Runspace
$Runspace.Open()
$PowerShell.AddScript({Start-Sleep 5})
$PowerShell.BeginInvoke()Runspaces'i Çalıştırmak: Bir Çözüm Yolu
Artık PowerShell komutunun elle tutulması gerekmediği için çalışma alanlarını kullanmak ilk başta göz korkutucu bir görev olabilir. .NET sınıflarıyla doğrudan ilgilenmeniz gerekecek. Bu bölümde PowerShell'de çalışma alanı oluşturmak için gerekenleri inceleyelim.
Bu izlenecek yolda PowerShell konsolunuzdan ayrı bir çalışma alanı ve ayrı bir PowerShell örneği oluşturacaksınız. Daha sonra yeni çalışma alanını yeni PowerShell örneğine atayacak ve bu örneğe kod ekleyeceksiniz.
Runspace'i oluşturun
Yapmanız gereken ilk şey yeni çalışma alanınızı oluşturmaktır. Bunu runspacefactory sınıfını kullanarak yaparsınız. Daha sonra başvurulabilmesi için bunu aşağıda gösterilene benzer bir değişkende saklayın.
$Runspace = [runspacefactory]::CreateRunspace()Artık çalışma alanı oluşturulduğuna göre, onu PowerShell kodunu çalıştırmak için bir PowerShell örneğine atayın. Bunun için powershell sınıfını kullanacaksınız ve runspace'e benzer şekilde bunu aşağıda gösterilene benzer bir değişkene kaydetmeniz gerekecek.
$PowerShell = [powershell]::Create()Ardından, çalışma alanını PowerShell örneğinize ekleyin, kodu çalıştırabilmek için çalışma alanını açın ve komut dosyası bloğunuzu ekleyin. Bu, aşağıda beş saniye boyunca uyuyacak bir komut dosyası bloğuyla gösterilmiştir.
$PowerShell.Runspace = $Runspace
$Runspace.Open()
$PowerShell.AddScript({Start-Sleep 5})Runspace'i yürütün
Şu ana kadar scriptblock hala çalıştırılmadı. Şu ana kadar yapılan tek şey, çalışma alanı için her şeyi tanımlamaktır. Betik bloğunu çalıştırmaya başlamak için iki seçeneğiniz vardır.
- Invoke() -
Invoke()yöntemi, komut dosyası bloğunu çalışma alanında çalıştırır ancak çalışma alanı dönene kadar konsola dönmeyi bekler. Bu, kodunuzu serbest bırakmadan önce düzgün şekilde çalıştığından emin olmak için yapılan testler için iyidir. - BeginInvoke() -
BeginInvoke()yöntemini kullanmak, gerçekten bir performans artışı görmek isteyeceğiniz şeydir. Bu, çalışma alanında çalışan scriptblock'u başlatacak ve sizi hemen konsola döndürecektir.
BeginInvoke() kullanırken, aşağıda gösterildiği gibi çalışma alanındaki komut dosyası bloğunun durumunu görmek için gerekli olacağından çıktıyı bir değişkene saklayın.
$Job = $PowerShell.BeginInvoke()BeginInvoke() çıktısını bir değişkene depoladıktan sonra, aşağıda IsCompleted özelliğinde görüldüğü gibi işin durumunu görmek için bu değişkeni kontrol edebilirsiniz.
Çıktıyı bir değişkende saklamanızın gerekmesinin bir diğer nedeni de Invoke() yönteminden farklı olarak BeginInvoke() yönteminin kod tamamlandığında çıktıyı otomatik olarak döndürmemesidir. Bunu yapmak için tamamlandıktan sonra EndInvoke() yöntemini kullanmanız gerekir.
Bu örnekte herhangi bir çıktı olmayacak ancak çağrıyı sonlandırmak için aşağıdaki komutu kullanacaksınız.
$PowerShell.EndInvoke($Job)Çalışma alanında sıraya koyduğunuz tüm görevler bittiğinde her zaman çalışma alanını kapatmalısınız. Bu, PowerShell'in otomatik çöp toplama işleminin kullanılmayan kaynakları temizlemesine olanak tanıyacaktır. Bunu yapmak için kullanacağınız komut aşağıdadır.
$Runspace.Close()Runspace Havuzlarını Kullanma
Bir çalışma alanı kullanmak performansı artırırken, tek bir iş parçacığının büyük bir sınırlamasıyla karşılaşır. Çalışma alanı havuzlarının birden fazla iş parçacığı kullanımında parladığı yer burasıdır.
Önceki bölümde yalnızca iki çalışma alanıyla kullanıyordunuz. PowerShell konsolu için yalnızca birini ve manuel olarak oluşturduğunuz konsolu kullandınız. Runspace havuzları, tek bir değişken kullanarak arka planda yönetilen birden fazla runspace'e sahip olmanızı sağlar.
Bu çoklu çalışma alanı davranışı birden fazla çalışma alanı nesnesiyle yapılabilirken, bir çalışma alanı havuzunun kullanılması yönetimi çok daha kolaylaştırır.
Çalışma alanı havuzları, kurulum şekilleri bakımından tekli çalışma alanlarından farklılık gösterir. Temel farklardan biri, çalışma alanı havuzu için kullanılabilecek maksimum iş parçacığı miktarını tanımlamanızdır. Tek bir çalışma alanıyla, tek bir iş parçacığıyla sınırlıdır, ancak bir havuzda, havuzun ölçeklenebileceği maksimum iş parçacığı miktarını siz belirlersiniz.
Bir çalışma alanı havuzunda önerilen iş parçacığı miktarı, gerçekleştirilen görevlerin miktarına ve kodu çalıştıran makineye bağlıdır. Maksimum iş parçacığı miktarını artırmak çoğu durumda hızı olumsuz etkilemese de hiçbir fayda da göremeyebilirsiniz.
Runspace Havuz Hızı Gösterimi
Bir çalışma alanı havuzunun tek bir çalışma alanını nerede yeneceğine dair bir örnek göstermek için belki on yeni dosya oluşturmak isteyebilirsiniz. Bu görev için tek bir çalışma alanı kullanırsanız, ilk dosyayı oluşturur, ardından ikinciye, ardından üçüncüye geçer ve on dosyanın tümü oluşturulana kadar bu şekilde devam edersiniz. Bu örneğin komut dosyası bloğu aşağıdaki gibi görünebilir. Bu komut dosyası bloğunu bir döngüde on dosya adı ile beslersiniz ve hepsi oluşturulur.
$Scriptblock = {
param($Name)
New-Item -Name $Name -ItemType File
}Aşağıdaki örnekte, bir adı kabul eden ve o adla bir dosya oluşturan kısa bir komut dosyasını içeren bir komut dosyası bloğu tanımlanmıştır. Maksimum 5 iş parçacığından oluşan bir çalışma alanı havuzu oluşturulur.
Daha sonra döngü on kez döner ve her seferinde yinelemenin numarasını $_'a atar. Yani ilk yinelemede 1, ikinci yinelemede 2 vb. olacaktır.
Döngü bir PowerShell nesnesi oluşturur, betik bloğunu ve betiğin argümanını atar ve işlemi başlatır.
Son olarak döngünün sonunda tüm kuyruk görevlerinin bitmesini bekleyecektir.
$Scriptblock = {
param($Name)
New-Item -Name $Name -ItemType File
}
$MaxThreads = 5
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads)
$RunspacePool.Open()
$Jobs = @()
1..10 | Foreach-Object {
$PowerShell = [powershell]::Create()
$PowerShell.RunspacePool = $RunspacePool
$PowerShell.AddScript($ScriptBlock).AddArgument($_)
$Jobs += $PowerShell.BeginInvoke()
}
while ($Jobs.IsCompleted -contains $false) {
Start-Sleep 1
}Artık iş parçacıklarını teker teker oluşturmak yerine, tek seferde beş tane oluşturacak. Çalışma alanı havuzları olmasaydı, beş ayrı çalışma alanı ve beş ayrı Powershell örneği oluşturup yönetmeniz gerekirdi. Bu yönetim hızla karmaşaya dönüşür.
Bunun yerine bir çalışma alanı havuzu, bir PowerShell örneği oluşturabilir, aynı kod bloğunu ve aynı döngüyü kullanabilirsiniz. Aradaki fark, çalışma alanının bu iş parçacıklarının beşini de kendi başına kullanacak şekilde ölçeklenmesidir.
Runspace Havuzları Oluşturma
Bir çalışma alanı havuzunun oluşturulması, önceki bölümde oluşturulan çalışma alanına çok benzer. Aşağıda bunun nasıl yapılacağına dair bir örnek verilmiştir. Bir komut dosyası bloğunun eklenmesi ve çağırma işlemi, bir çalışma alanıyla aynıdır. Aşağıda görebileceğiniz gibi runspace havuzu maksimum beş thread ile oluşturuluyor.
$MaxThreads = 5
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads)
$PowerShell = [powershell]::Create()
$PowerShell.RunspacePool = $RunspacePool
$RunspacePool.Open()Hız Açısından Runspace'leri ve Runspace Havuzlarını Karşılaştırma
Bir çalışma alanı ile çalışma alanı havuzu arasındaki farkı göstermek için bir çalışma alanı oluşturun ve daha önce kullandığınız Uyku Başlat komutunu çalıştırın. Ancak bu sefer 10 kez çalıştırılması gerekiyor. Aşağıdaki kodda görebileceğiniz gibi 5 saniye uyuyacak bir runspace oluşturuluyor.
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.Runspace = $Runspace
$Runspace.Open()
$PowerShell.AddScript({Start-Sleep 5})
1..10 | Foreach-Object {
$Job = $PowerShell.BeginInvoke()
while ($Job.IsCompleted -eq $false) {Start-Sleep -Milliseconds 100}
}Tek bir çalışma alanı kullandığınızdan, başka bir çağrı başlatılmadan önce bunun tamamlanmasını beklemeniz gerekeceğini unutmayın. Bu nedenle iş tamamlanana kadar 100 ms'lik bir uyku eklenir. Bu azaltılabilse de, işin bitmesini beklemek yerine işin yapılıp yapılmadığını kontrol etmek için daha fazla zaman harcayacağınız için getirilerin azaldığını göreceksiniz.
Aşağıdaki örnekte, 10 set 5 saniyelik uykuyu tamamlamanın yaklaşık 51 saniye sürdüğünü görebilirsiniz.
Artık tek bir çalışma alanı kullanmak yerine bir çalışma alanı havuzuna geçin. Çalıştırılacak kod aşağıdadır. Runspace havuzu kullanırken aşağıdaki kodda ikisinin kullanımı arasında birkaç fark olduğunu görebilirsiniz.
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, 5)
$RunspacePool.Open()
$Jobs = @()
1..10 | Foreach-Object {
$PowerShell = [powershell]::Create()
$PowerShell.RunspacePool = $RunspacePool
$PowerShell.AddScript({Start-Sleep 5})
$Jobs += $PowerShell.BeginInvoke()
}
while ($Jobs.IsCompleted -contains $false) {Start-Sleep -Milliseconds 100}Aşağıda da görebileceğiniz gibi, bu işlem 10 saniyenin biraz üzerinde bir sürede tamamlanıyor; bu, tek çalışma alanı için 51 saniyeye kıyasla çok daha iyi bir rakam.
Aşağıda, bu örneklerde bir çalışma alanı ile çalışma alanı havuzu arasındaki farkın bir döküm özeti bulunmaktadır.
| Property | Runspace | Runspace Pool |
|---|---|---|
| Wait Delay | Waiting for each job to finish before continuing to the next. | Starting all of the jobs and then waiting until they have all finished. |
| Amount of Threads | One | Five |
| Runtime | 50.8 Seconds | 10.1 Seconds |
PoshRSJob ile Runspace'lere Geçiş
Programlama sırasında sık sık karşılaşılan bir durum, daha rahat olanı yapmanız ve performanstaki küçük kaybı kabul etmenizdir. Bunun nedeni kodun yazılmasını veya okunmasını kolaylaştırması olabilir ya da sadece sizin tercihiniz olabilir.
Aynı şey, bazı kişilerin kullanım kolaylığı nedeniyle çalışma alanları yerine PSJobs'u kullanacağı PowerShell'de de oluyor. Farkı paylaşmak ve kullanımı çok daha zorlaştırmadan daha iyi performans elde etmek için yapılabilecek birkaç şey vardır.
PoshRSJob adında, normal PSJob'ların stiliyle eşleşen ancak çalışma alanlarını kullanmanın ek avantajına sahip modüller içeren, yaygın olarak kullanılan bir modül vardır. Çalışma alanını ve powershell nesnesini oluşturmak için tüm kodu belirtmek zorunda kalmak yerine, PoshRSJob modülü, komutları çalıştırdığınızda tüm bunları gerçekleştirir.
Modülü yüklemek için yönetimsel bir PowerShell oturumunda aşağıdaki komutu çalıştırın.
Install-Module PoshRSJobModül yüklendikten sonra komutların RS önekiyle PSJob komutlarıyla aynı olduğunu görebilirsiniz. Start-Job yerine Start-RSJob'dur. Get-Job yerine Get-RSJob'dur.
Aşağıda aynı komutun bir PSJob'da ve ardından bir RSJob'da nasıl çalıştırılacağına dair bir örnek verilmiştir. Gördüğünüz gibi, çok benzer sözdizimlerine ve çıktılara sahipler, ancak tamamen aynı değiller.
Aşağıda PSJob ile RSJob arasındaki hız farkını karşılaştırmak için kullanılabilecek bazı kodlar bulunmaktadır.
Measure-Command {Start-Job -ScriptBlock {Start-Sleep 5}}
Measure-Command {Start-RSJob -ScriptBlock {Start-Sleep 5}}Aşağıda görebileceğiniz gibi, RSJob'lar hala kapakların altındaki çalışma alanlarını kullandığından büyük bir hız farkı var.
Foreach-Nesne -Paralel
PowerShell topluluğu, bir süreci hızlı bir şekilde çoklu iş parçacıklarından geçirmenin daha kolay ve yerleşik bir yolunu arıyordu. Paralel anahtar bundan çıkan şeydir.
Bunu yazarken PowerShell 7 hala önizleme aşamasındadır ancak Foreach-Object komutuna bir Parallel parametresi eklenmiştir. Bu işlem, kodu paralelleştirmek için çalışma alanlarını kullanır ve çalışma alanı için komut dosyası bloğu olarak Foreach-Object için kullanılan komut dosyası bloğunu kullanır.
Ayrıntılar hala üzerinde çalışılıyor olsa da bu, gelecekte çalışma alanlarını kullanmanın daha kolay bir yolu olabilir. Aşağıda görebileceğiniz gibi, birçok uyku kümesi arasında hızlı bir şekilde geçiş yapabilirsiniz.
Measure-Command {1..10 | Foreach-Object {Start-Sleep 5}}
Measure-Command {1..10 | Foreach-Object -Parallel {Start-Sleep 5}}Çoklu İş Parçacığı İle İlgili Zorluklar
Çoklu iş parçacığı kullanımı şu ana kadar harika gibi görünse de durum tam olarak böyle değil. Herhangi bir kodun çoklu iş parçacıklarını kullanmanın beraberinde getirdiği birçok zorluk vardır.
Değişkenleri Kullanma
Çoklu iş parçacığının en büyük ve en belirgin zorluklarından biri, değişkenleri argüman olarak iletmeden paylaşamamanızdır. Senkronize hashtable'ın tek istisnası var ama bu başka bir günün konusu.
Hem PSJob'lar hem de çalışma alanları, mevcut değişkenlere herhangi bir erişim olmadan çalışır ve farklı çalışma alanlarında kullanılan değişkenlerle konsolunuzdan etkileşim kurmanın bir yolu yoktur.
Bu, bilgilerin bu işlere dinamik olarak aktarılması açısından büyük bir zorluk teşkil etmektedir. Cevap, kullandığınız çoklu iş parçacığına bağlı olarak farklıdır.
PoshRSJob modülündeki Start-Job ve Start-RSJob için, iletilecek nesnelerin listesini sağlamak üzere ArgumentList parametresini kullanabilirsiniz. bunları listelediğiniz sıraya göre komut dosyası bloğuna parametreler olarak ekleyin. Aşağıda PSJob'lar ve RSJob'lar için kullanılan komutların örnekleri verilmiştir.
PSİş :
Start-Job -Scriptblock {param ($Text) Write-Output $Text} -ArgumentList "Hello world!"RSİşi :
Start-RSJob -Scriptblock {param ($Text) Write-Output $Text} -ArgumentList "Hello world!"Navigasyon çalışma alanları size aynı kolaylığı sağlamaz. Bunun yerine PowerShell nesnesinde AddArgument() yöntemini kullanmanız gerekir. Aşağıda her biri için nasıl görüneceğine dair bir örnek verilmiştir.
Çalışma alanı:
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.Runspace = $Runspace
$Runspace.Open()
$PowerShell.AddScript({param ($Text) Write-Output $Text})
$PowerShell.AddArgument("Hello world!")
$PowerShell.BeginInvoke()Çalışma alanı havuzları aynı şekilde çalışsa da aşağıda bir çalışma alanı havuzuna nasıl bağımsız değişken ekleneceğine ilişkin bir örnek verilmiştir.
$MaxThreads = 5
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads)
$PowerShell = [powershell]::Create()
$PowerShell.RunspacePool = $RunspacePool
$RunspacePool.Open()
$PowerShell.AddScript({param ($Text) Write-Output $Text})
$PowerShell.AddArgument("Hello world!")
$PowerShell.BeginInvoke()Kerestecilik
Çoklu iş parçacığı aynı zamanda günlüğe kaydetme zorluklarını da beraberinde getirir. Her iş parçacığı birbirinden bağımsız çalıştığı için hepsi aynı yere giriş yapamaz. Birden fazla iş parçacığı içeren bir dosyayı söylemek için oturum açmayı denediyseniz, bir iş parçacığı dosyaya yazarken başka iş parçacığı yazamazdı. Bu, kodunuzu yavaşlatabilir veya tamamen başarısız olmasına neden olabilir.
Örnek olarak aşağıda, bir çalışma alanı havuzundaki 5 iş parçacığını kullanarak tek bir dosyaya 100 kez oturum açmaya çalışan bazı kodlar verilmiştir.
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, 5)
$RunspacePool.Open()
1..100 | Foreach-Object {
$PowerShell = [powershell]::Create().AddScript({'Hello' | Out-File -Append -FilePath .\Test.txt})
$PowerShell.RunspacePool = $RunspacePool
$PowerShell.BeginInvoke()
}
$RunspacePool.Close()Çıktıda herhangi bir hata görmezsiniz ancak metin dosyasının boyutuna bakarsanız, aşağıda 100 işin tamamının doğru şekilde tamamlanmadığını görebilirsiniz.
Bunu aşmanın bazı yolları ayrı dosyalara giriş yapmaktır. Bu, dosya kilitleme sorununu ortadan kaldırır, ancak daha sonra olup biteni anlamak için sıralamanız gereken birçok günlük dosyanız olur.
Diğer bir alternatif ise bazı çıktıların zamanlamasının kapalı olmasına izin vermeniz ve işin ne yaptığını yalnızca bittiğinde kaydetmenizdir. Bu, orijinal oturumunuz aracılığıyla her şeyin serileştirilmesine olanak tanır, ancak her şeyin hangi sırayla gerçekleştiğini tam olarak bilemediğiniz için bazı ayrıntıları kaybedersiniz.
Özet
Çoklu iş parçacığı büyük performans kazanımları sağlayabilirken aynı zamanda baş ağrısına da neden olabilir. Bazı iş yükleri büyük fayda sağlarken bazıları hiç fayda sağlamayabilir. Çoklu iş parçacığını kullanmanın birçok artısı ve eksisi vardır, ancak doğru kullanılırsa kodunuzun çalışma süresini önemli ölçüde azaltabilirsiniz.
Daha fazla okuma
- PowerShell 7 Önizleme 3 - Paralel Ne Zaman Doğdu
- Komut Dosyalarınız için PowerShell GUI'si Nasıl Oluşturulur?












