Aracılığıyla paylaş


Foreach Parallel ile birden çok iş parçacığı arasında Yazma İlerlemesi

PowerShell 7.0'dan başlayarak, Foreach-Object cmdlet'indeki Parallel parametresi kullanılarak birden çok iş parçacığında aynı anda çalışma olanağı mümkündür. Ancak bu iş parçacıklarının ilerleme durumunu izlemek zor olabilir. Normalde, Yazma İlerlemesi'ni kullanarak bir işlemin ilerleme durumunu izleyebilirsiniz. Bununla birlikte, PowerShell Paralel kullanırken her iş parçacığı için ayrı bir çalışma alanı kullandığından, ilerleme durumunu konağa geri raporlamak normal kullanımı Write-Progresskadar düz değildir.

İlerleme durumunu izlemek için eşitlenmiş karma tablo kullanma

Birden çok iş parçacığından ilerlemeyi yazarken, PowerShell'de paralel işlemler çalıştırılırken her işlemin kendi çalışma alanı olduğundan izleme zorlaşır. Bu sorunu çözmek için eşitlenmiş bir karma tablo kullanabilirsiniz. Eşitlenmiş karma tablo, hata oluşturmadan aynı anda birden çok iş parçacığı tarafından değiştirilebilen iş parçacığı güvenli veri yapısıdır.

Ayarlama

Bu yaklaşımın dezavantajlarından biri, her şeyin hatasız çalıştığından emin olmak için biraz karmaşık bir kurulum yapmaktır.

$dataset = @(
    @{
        Id   = 1
        Wait = 3..10 | get-random | Foreach-Object {$_*100}
    }
    @{
        Id   = 2
        Wait = 3..10 | get-random | Foreach-Object {$_*100}
    }
    @{
        Id   = 3
        Wait = 3..10 | get-random | Foreach-Object {$_*100}
    }
    @{
        Id   = 4
        Wait = 3..10 | get-random | Foreach-Object {$_*100}
    }
    @{
        Id   = 5
        Wait = 3..10 | get-random | Foreach-Object {$_*100}
    }
)

# Create a hashtable for process.
# Keys should be ID's of the processes
$origin = @{}
$dataset | Foreach-Object {$origin.($_.id) = @{}}

# Create synced hashtable
$sync = [System.Collections.Hashtable]::Synchronized($origin)

Bu bölüm, üç farklı amaçla üç farklı veri yapısı oluşturur.

değişkeni, $dataSet değiştirilme riski olmadan sonraki adımları koordine etmek için kullanılan bir karma tablo dizisi depolar. Bir nesne koleksiyonu, koleksiyonda yinelenirken değiştirilirse PowerShell bir hata oluşturur. Döngüdeki nesne koleksiyonunu değiştirilmekte olan nesnelerden ayrı tutmalısınız. Id Her karma tablodaki anahtar, sahte işlemin tanımlayıcısıdır. Anahtar, Wait izlenen her sahte işlemin iş yükünün simülasyonunu oluşturur.

değişkeni, $origin her anahtarın sahte işlem kimliklerinden biri olduğu iç içe yerleştirilmiş bir karma tablo depolar. Ardından, değişkende depolanan eşitlenmiş karmatable'ı nemlendirmek $sync için kullanılır. $sync Değişken, ilerleme durumunu üst çalışma alanı olarak raporlamak ve ilerleme durumunu görüntülemekle sorumludur.

İşlemleri çalıştırma

Bu bölüm çok iş parçacıklı işlemleri çalıştırır ve ilerleme durumunu görüntülemek için kullanılan çıkışın bir kısmını oluşturur.

$job = $dataset | Foreach-Object -ThrottleLimit 3 -AsJob -Parallel {
    $syncCopy = $using:sync
    $process = $syncCopy.$($PSItem.Id)

    $process.Id = $PSItem.Id
    $process.Activity = "Id $($PSItem.Id) starting"
    $process.Status = "Processing"

    # Fake workload start up that takes x amount of time to complete
    start-sleep -Milliseconds ($PSItem.wait*5)

    # Process. update activity
    $process.Activity = "Id $($PSItem.id) processing"
    foreach ($percent in 1..100)
    {
        # Update process on status
        $process.Status = "Handling $percent/100"
        $process.PercentComplete = (($percent / 100) * 100)

        # Fake workload that takes x amount of time to complete
        Start-Sleep -Milliseconds $PSItem.Wait
    }

    # Mark process as completed
    $process.Completed = $true
}

Sahte işlemler iş Foreach-Object olarak gönderilir ve başlatılır. Bir kuyrukta birden çok işlemin çalıştırılmasını vurgulamak için ThrottleLimit 3 olarak ayarlanır. İşler değişkeninde $job depolanır ve daha sonra tüm işlemlerin ne zaman tamamlandığını bilmemize olanak tanır.

PowerShell'de using: üst kapsam değişkenine başvurmak için deyimini kullanırken, bunu dinamik hale getirmek için ifadeleri kullanamazsınız. Örneğin, gibi bir değişken oluşturmaya $process çalışırsanız, $process = $using:sync.$($PSItem.id)burada ifadeleri kullanamadığını belirten bir hatayla karşılaşırsınız. Bu nedenle, değişkene $syncCopy başvurmak ve değişkenini başarısız olma riski olmadan değiştirebilmek $sync için değişkeni oluştururuz.

Ardından, eşitlenmiş karma tablo anahtarlarına başvurarak değişkenini kullanarak $process döngüde olan işlemin ilerleme durumunu temsil eden bir karma tablo oluşturacağız. Activity ve Status anahtarları, sonraki bölümde belirli bir sahte işlemin durumunu görüntülemek için Write-Progress parametresi değerleri olarak kullanılır.

Döngüforeach, işlemin çalışmasının benzetimini yapmak için kullanılan bir yöntemdir ve milisaniyeler kullanılarak ayarlamak Start-Sleep için Wait özniteliğine göre $dataSet rastgele oluşturulur. İşleminizin ilerleme durumunu hesaplama şekliniz farklılık gösterebilir.

Birden çok işlemin ilerleme durumunu görüntüleme

Artık sahte işlemler iş olarak çalıştığına göre, işlemlerin ilerleme durumunu PowerShell penceresine yazmaya başlayabiliriz.

while($job.State -eq 'Running')
{
    $sync.Keys | Foreach-Object {
        # If key is not defined, ignore
        if(![string]::IsNullOrEmpty($sync.$_.keys))
        {
            # Create parameter hashtable to splat
            $param = $sync.$_

            # Execute Write-Progress
            Write-Progress @param
        }
    }

    # Wait to refresh to not overload gui
    Start-Sleep -Seconds 0.1
}

$job değişkeni üst işi içerir ve sahte işlemlerin her biri için bir alt işe sahiptir. Alt işlerden herhangi biri çalışmaya devam ederken, üst iş Durumu "Çalışıyor" olarak kalır. Bu, tüm işlemler tamamlanana kadar her işlemin ilerleme durumunu sürekli olarak güncelleştirmek için döngüsünü kullanmamıza while olanak tanır.

while döngüsü içinde değişkendeki $sync anahtarların her biri arasında döngü yapıyoruz. Bu eşitlenmiş bir karma tablo olduğundan, sürekli olarak güncelleştirilir, ancak herhangi bir hata oluşturmadan yine erişilebilir.

Bildirilen işlemin yöntemini kullanarak IsNullOrEmpty() gerçekten çalıştığından emin olmak için bir denetim vardır. İşlem başlatılmadıysa, döngü üzerinde rapor vermez ve başlatılan bir işleme gelene kadar sonrakine geçer. İşlem başlatılırsa, parametreleri Write-Progress'ye eklemek için geçerli anahtardan karma tablo kullanılır.

Tam örnek

# Example workload
$dataset = @(
    @{
        Id   = 1
        Wait = 3..10 | get-random | Foreach-Object {$_*100}
    }
    @{
        Id   = 2
        Wait = 3..10 | get-random | Foreach-Object {$_*100}
    }
    @{
        Id   = 3
        Wait = 3..10 | get-random | Foreach-Object {$_*100}
    }
    @{
        Id   = 4
        Wait = 3..10 | get-random | Foreach-Object {$_*100}
    }
    @{
        Id   = 5
        Wait = 3..10 | get-random | Foreach-Object {$_*100}
    }
)

# Create a hashtable for process.
# Keys should be ID's of the processes
$origin = @{}
$dataset | Foreach-Object {$origin.($_.id) = @{}}

# Create synced hashtable
$sync = [System.Collections.Hashtable]::Synchronized($origin)

$job = $dataset | Foreach-Object -ThrottleLimit 3 -AsJob -Parallel {
    $syncCopy = $using:sync
    $process = $syncCopy.$($PSItem.Id)

    $process.Id = $PSItem.Id
    $process.Activity = "Id $($PSItem.Id) starting"
    $process.Status = "Processing"

    # Fake workload start up that takes x amount of time to complete
    start-sleep -Milliseconds ($PSItem.wait*5)

    # Process. update activity
    $process.Activity = "Id $($PSItem.id) processing"
    foreach ($percent in 1..100)
    {
        # Update process on status
        $process.Status = "Handling $percent/100"
        $process.PercentComplete = (($percent / 100) * 100)

        # Fake workload that takes x amount of time to complete
        Start-Sleep -Milliseconds $PSItem.Wait
    }

    # Mark process as completed
    $process.Completed = $true
}

while($job.State -eq 'Running')
{
    $sync.Keys | Foreach-Object {
        # If key is not defined, ignore
        if(![string]::IsNullOrEmpty($sync.$_.keys))
        {
            # Create parameter hashtable to splat
            $param = $sync.$_

            # Execute Write-Progress
            Write-Progress @param
        }
    }

    # Wait to refresh to not overload gui
    Start-Sleep -Seconds 0.1
}