From 13d93d3220625a77af8575bea12ca70593ef276b Mon Sep 17 00:00:00 2001 From: Sean Wheeler Date: Mon, 4 Apr 2022 10:55:46 -0500 Subject: [PATCH 1/3] Explain the effect of runspaces on script methods and properties --- .../ForEach-Object.md | 68 +++++++---- .../ForEach-Object.md | 72 +++++++----- .../ForEach-Object.md | 109 +++++++++++++----- .../ForEach-Object.md | 109 +++++++++++++----- 4 files changed, 254 insertions(+), 104 deletions(-) diff --git a/reference/7.0/Microsoft.PowerShell.Core/ForEach-Object.md b/reference/7.0/Microsoft.PowerShell.Core/ForEach-Object.md index 9b5464cb92a6..0f8550e99103 100644 --- a/reference/7.0/Microsoft.PowerShell.Core/ForEach-Object.md +++ b/reference/7.0/Microsoft.PowerShell.Core/ForEach-Object.md @@ -2,7 +2,7 @@ external help file: System.Management.Automation.dll-Help.xml Locale: en-US Module Name: Microsoft.PowerShell.Core -ms.date: 07/27/2021 +ms.date: 04/04/2022 online version: https://docs.microsoft.com/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7&WT.mc_id=ps-gethelp schema: 2.0.0 title: ForEach-Object @@ -81,19 +81,7 @@ command. By default, the parallel scriptblocks use the current working directory of the caller that started the parallel tasks. - Non-terminating errors are written to the cmdlet error stream as they occur in parallel running - scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in - which errors appear in the error stream is random. Likewise, messages written to other data - streams, like warning, verbose, or information are written to those data streams in an - indeterminate order. - - Terminating errors, such as exceptions, terminate the individual parallel instance of the - scriptblocks in which they occur. A terminating error in one scriptblocks may not cause the - termination of the `Foreach-Object` cmdlet. The other scriptblocks, running in parallel, continue - to run unless they also encounter a terminating error. The terminating error is written to the - error data stream as an **ErrorRecord** with a **FullyQualifiedErrorId** of `PSTaskException`. - Terminating errors can be converted to non-terminating errors using PowerShell try/catch or trap - blocks. + For more information, see the [NOTES](#notes) section of this article. ## EXAMPLES @@ -444,6 +432,10 @@ Output: 5 `Output: 3` is never written because the parallel scriptblock for that iteration was terminated. +> [!NOTE] +> [PipelineVariable](About/about_CommonParameters.md) common parameter variables are _not_ +> supported in `Foreach-Object -Parallel` scenarios even with the `$using:` keyword. + ### Example 17: Passing variables in nested parallel script ScriptBlockSet You can create a variable outside a `Foreach-Object -Parallel` scoped scriptblock and use @@ -765,17 +757,19 @@ This cmdlet returns objects that are determined by the input. ## NOTES -- The `ForEach-Object` cmdlet works much like the **Foreach** statement, except that you cannot pipe - input to a **Foreach** statement. For more information about the **Foreach** statement, see - [about_Foreach](./About/about_Foreach.md). +The `ForEach-Object` cmdlet works much like the **Foreach** statement, except that you cannot pipe +input to a **Foreach** statement. For more information about the **Foreach** statement, see +[about_Foreach](./About/about_Foreach.md). -- Starting in PowerShell 4.0, `Where` and `ForEach` methods were added for use with collections. You - can read more about these new methods here [about_arrays](./About/about_Arrays.md) +Starting in PowerShell 4.0, `Where` and `ForEach` methods were added for use with collections. You +can read more about these new methods here [about_arrays](./About/about_Arrays.md) + +Using `ForEach-Object -Parallel`: - The `ForEach-Object -Parallel` parameter set uses PowerShell's internal API to run each script - block. This is significantly more overhead than running `ForEach-Object` normally with sequential - processing. It is important to use **Parallel** where the overhead of running in parallel is small - compared to work the script block performs. For example: + block in a new runspace. This is significantly more overhead than running `ForEach-Object` + normally with sequential processing. It is important to use **Parallel** where the overhead of + running in parallel is small compared to work the script block performs. For example: - Compute intensive scripts on multi-core machines - Scripts that spend time waiting for results or doing file operations @@ -784,8 +778,34 @@ This cmdlet returns objects that are determined by the input. the parallel scripts are trivial. Experiment with **Parallel** to discover where it can be beneficial. +- When running in parallel, objects decorated with **ScriptProperties** or **ScriptMethods** cannot + be guaranteed to function correctly if they are run in a different runspace than the scripts were + originally attached to them. + + Scriptblock invocation always attempts to run in its _home_ runspace, regardless of where it's + actually invoked. However, `ForEach-Object -Parallel` creates temporary runspaces that get deleted + after use, so there's no runspace for the scripts to execute in anymore. + + This behavior can work if as long as the _home_ runspace still exists. However, you may not get + the desired result if the script is dependent on external variables that are only present in the + caller's runspace and not the _home_ runspace. + +- Non-terminating errors are written to the cmdlet error stream as they occur in parallel running + scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in + which errors appear in the error stream is random. Likewise, messages written to other data + streams, like warning, verbose, or information are written to those data streams in an + indeterminate order. + + Terminating errors, such as exceptions, terminate the individual parallel instance of the + scriptblocks in which they occur. A terminating error in one scriptblocks may not cause the + termination of the `Foreach-Object` cmdlet. The other scriptblocks, running in parallel, continue + to run unless they also encounter a terminating error. The terminating error is written to the + error data stream as an **ErrorRecord** with a **FullyQualifiedErrorId** of `PSTaskException`. + Terminating errors can be converted to non-terminating errors using PowerShell try/catch or trap + blocks. + - [PipelineVariable](About/about_CommonParameters.md) common parameter variables are _not_ supported - in `Foreach-Object -Parallel` scenarios even with the `$using:` keyword. + in parallel scenarios even with the `$using:` keyword. > [!IMPORTANT] > The `ForEach-Object -Parallel` parameter set runs script blocks in parallel on separate process @@ -793,7 +813,7 @@ This cmdlet returns objects that are determined by the input. > thread to each running script block thread. Since the script blocks run in different threads, > the object variables passed by reference must be used safely. Generally it is safe to read from > referenced objects that don't change. But if the object state is being modified then you must - > used thread safe objects, such as .Net **System.Collection.Concurrent** types (See Example 11). + > used thread safe objects, such as .NET **System.Collection.Concurrent** types (See Example 11). ## RELATED LINKS diff --git a/reference/7.1/Microsoft.PowerShell.Core/ForEach-Object.md b/reference/7.1/Microsoft.PowerShell.Core/ForEach-Object.md index c944c343efdf..c27c01285bf9 100644 --- a/reference/7.1/Microsoft.PowerShell.Core/ForEach-Object.md +++ b/reference/7.1/Microsoft.PowerShell.Core/ForEach-Object.md @@ -2,7 +2,7 @@ external help file: System.Management.Automation.dll-Help.xml Locale: en-US Module Name: Microsoft.PowerShell.Core -ms.date: 07/27/2021 +ms.date: 04/04/2022 online version: https://docs.microsoft.com/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.1&WT.mc_id=ps-gethelp schema: 2.0.0 title: ForEach-Object @@ -31,8 +31,8 @@ ForEach-Object [-InputObject ] [-MemberName] [-ArgumentList < ### ParallelParameterSet ``` -ForEach-Object -Parallel [-InputObject ] [-ThrottleLimit ] - [-UseNewRunspace] [-TimeoutSeconds ] [-AsJob] [-WhatIf] [-Confirm] [] +ForEach-Object [-InputObject ] -Parallel [-ThrottleLimit ] + [-TimeoutSeconds ] [-AsJob] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -84,19 +84,7 @@ command. By default, the parallel scriptblocks use the current working directory of the caller that started the parallel tasks. - Non-terminating errors are written to the cmdlet error stream as they occur in parallel running - scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in - which errors appear in the error stream is random. Likewise, messages written to other data - streams, like warning, verbose, or information are written to those data streams in an - indeterminate order. - - Terminating errors, such as exceptions, terminate the individual parallel instance of the - scriptblocks in which they occur. A terminating error in one scriptblocks may not cause the - termination of the `Foreach-Object` cmdlet. The other scriptblocks, running in parallel, continue - to run unless they also encounter a terminating error. The terminating error is written to the - error data stream as an **ErrorRecord** with a **FullyQualifiedErrorId** of `PSTaskException`. - Terminating errors can be converted to non-terminating errors using PowerShell try/catch or trap - blocks. + For more information, see the [NOTES](#notes) section of this article. ## EXAMPLES @@ -447,6 +435,10 @@ Output: 5 `Output: 3` is never written because the parallel scriptblock for that iteration was terminated. +> [!NOTE] +> [PipelineVariable](About/about_CommonParameters.md) common parameter variables are _not_ +> supported in `Foreach-Object -Parallel` scenarios even with the `$using:` keyword. + ### Example 17: Passing variables in nested parallel script ScriptBlockSet You can create a variable outside a `Foreach-Object -Parallel` scoped scriptblock and use @@ -787,17 +779,19 @@ This cmdlet returns objects that are determined by the input. ## NOTES -- The `ForEach-Object` cmdlet works much like the **Foreach** statement, except that you cannot pipe - input to a **Foreach** statement. For more information about the **Foreach** statement, see - [about_Foreach](./About/about_Foreach.md). +The `ForEach-Object` cmdlet works much like the **Foreach** statement, except that you cannot pipe +input to a **Foreach** statement. For more information about the **Foreach** statement, see +[about_Foreach](./About/about_Foreach.md). -- Starting in PowerShell 4.0, `Where` and `ForEach` methods were added for use with collections. You - can read more about these new methods here [about_arrays](./About/about_Arrays.md) +Starting in PowerShell 4.0, `Where` and `ForEach` methods were added for use with collections. You +can read more about these new methods here [about_arrays](./About/about_Arrays.md) + +Using `ForEach-Object -Parallel`: - The `ForEach-Object -Parallel` parameter set uses PowerShell's internal API to run each script - block. This is significantly more overhead than running `ForEach-Object` normally with sequential - processing. It is important to use **Parallel** where the overhead of running in parallel is small - compared to work the script block performs. For example: + block in a new runspace. This is significantly more overhead than running `ForEach-Object` + normally with sequential processing. It is important to use **Parallel** where the overhead of + running in parallel is small compared to work the script block performs. For example: - Compute intensive scripts on multi-core machines - Scripts that spend time waiting for results or doing file operations @@ -806,8 +800,34 @@ This cmdlet returns objects that are determined by the input. the parallel scripts are trivial. Experiment with **Parallel** to discover where it can be beneficial. +- When running in parallel, objects decorated with **ScriptProperties** or **ScriptMethods** cannot + be guaranteed to function correctly if they are run in a different runspace than the scripts were + originally attached to them. + + Scriptblock invocation always attempts to run in its _home_ runspace, regardless of where it's + actually invoked. However, `ForEach-Object -Parallel` creates temporary runspaces that get deleted + after use, so there's no runspace for the scripts to execute in anymore. + + This behavior can work if as long as the _home_ runspace still exists. However, you may not get + the desired result if the script is dependent on external variables that are only present in the + caller's runspace and not the _home_ runspace. + +- Non-terminating errors are written to the cmdlet error stream as they occur in parallel running + scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in + which errors appear in the error stream is random. Likewise, messages written to other data + streams, like warning, verbose, or information are written to those data streams in an + indeterminate order. + + Terminating errors, such as exceptions, terminate the individual parallel instance of the + scriptblocks in which they occur. A terminating error in one scriptblocks may not cause the + termination of the `Foreach-Object` cmdlet. The other scriptblocks, running in parallel, continue + to run unless they also encounter a terminating error. The terminating error is written to the + error data stream as an **ErrorRecord** with a **FullyQualifiedErrorId** of `PSTaskException`. + Terminating errors can be converted to non-terminating errors using PowerShell try/catch or trap + blocks. + - [PipelineVariable](About/about_CommonParameters.md) common parameter variables are _not_ supported - in `Foreach-Object -Parallel` scenarios even with the `$using:` keyword. + in parallel scenarios even with the `$using:` keyword. > [!IMPORTANT] > The `ForEach-Object -Parallel` parameter set runs script blocks in parallel on separate process @@ -815,7 +835,7 @@ This cmdlet returns objects that are determined by the input. > thread to each running script block thread. Since the script blocks run in different threads, > the object variables passed by reference must be used safely. Generally it is safe to read from > referenced objects that don't change. But if the object state is being modified then you must - > used thread safe objects, such as .Net **System.Collection.Concurrent** types (See Example 11). + > used thread safe objects, such as .NET **System.Collection.Concurrent** types (See Example 11). ## RELATED LINKS diff --git a/reference/7.2/Microsoft.PowerShell.Core/ForEach-Object.md b/reference/7.2/Microsoft.PowerShell.Core/ForEach-Object.md index 764885af9179..bed27981dab6 100644 --- a/reference/7.2/Microsoft.PowerShell.Core/ForEach-Object.md +++ b/reference/7.2/Microsoft.PowerShell.Core/ForEach-Object.md @@ -2,7 +2,7 @@ external help file: System.Management.Automation.dll-Help.xml Locale: en-US Module Name: Microsoft.PowerShell.Core -ms.date: 07/27/2021 +ms.date: 04/04/2022 online version: https://docs.microsoft.com/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.2&WT.mc_id=ps-gethelp schema: 2.0.0 title: ForEach-Object @@ -31,8 +31,8 @@ ForEach-Object [-InputObject ] [-MemberName] [-ArgumentList < ### ParallelParameterSet ``` -ForEach-Object -Parallel [-InputObject ] [-ThrottleLimit ] - [-UseNewRunspace] [-TimeoutSeconds ] [-AsJob] [-WhatIf] [-Confirm] [] +ForEach-Object [-InputObject ] -Parallel [-ThrottleLimit ] + [-TimeoutSeconds ] [-AsJob] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -84,19 +84,7 @@ command. By default, the parallel scriptblocks use the current working directory of the caller that started the parallel tasks. - Non-terminating errors are written to the cmdlet error stream as they occur in parallel running - scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in - which errors appear in the error stream is random. Likewise, messages written to other data - streams, like warning, verbose, or information are written to those data streams in an - indeterminate order. - - Terminating errors, such as exceptions, terminate the individual parallel instance of the - scriptblocks in which they occur. A terminating error in one scriptblocks may not cause the - termination of the `Foreach-Object` cmdlet. The other scriptblocks, running in parallel, continue - to run unless they also encounter a terminating error. The terminating error is written to the - error data stream as an **ErrorRecord** with a **FullyQualifiedErrorId** of `PSTaskException`. - Terminating errors can be converted to non-terminating errors using PowerShell try/catch or trap - blocks. + For more information, see the [NOTES](#notes) section of this article. ## EXAMPLES @@ -233,7 +221,7 @@ operator `$_`. It uses the dot syntax to specify the method and parentheses to e argument. The second command uses the **MemberName** parameter to specify the **Split** method and the -**ArgumentList** parameter to identify the dot (".") as the split delimiter. +**ArgumentList** parameter to identify the dot (`.`) as the split delimiter. The third command uses the **Foreach** alias of the `ForEach-Object` cmdlet and omits the names of the **MemberName** and **ArgumentList** parameters, which are optional. @@ -451,6 +439,45 @@ Output: 5 > [PipelineVariable](About/about_CommonParameters.md) common parameter variables are _not_ > supported in `Foreach-Object -Parallel` scenarios even with the `$using:` keyword. +### Example 17: Passing variables in nested parallel script ScriptBlockSet + +You can create a variable outside a `Foreach-Object -Parallel` scoped scriptblock and use +it inside the scriptblock with the `$using` keyword. + +```powershell +$test1 = 'TestA' +1..2 | Foreach-Object -Parallel { + $using:test1 +} +``` + +```Output +TestA +TestA +``` + +```powershell +# You CANNOT create a variable inside a scoped scriptblock +# to be used in a nested foreach parallel scriptblock. +$test1 = 'TestA' +1..2 | Foreach-Object -Parallel { + $using:test1 + $test2 = 'TestB' + 1..2 | Foreach-Object -Parallel { + $using:test2 + } +} +``` + +```Output +Line | + 2 | 1..2 | Foreach-Object -Parallel { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + | The value of the using variable '$using:test2' cannot be retrieved because it has not been set in the local session. +``` + +The nested scriptblock can't access the `$test2` variable and an error is thrown. + ## PARAMETERS ### -ArgumentList @@ -752,17 +779,19 @@ This cmdlet returns objects that are determined by the input. ## NOTES -- The `ForEach-Object` cmdlet works much like the **Foreach** statement, except that you cannot pipe - input to a **Foreach** statement. For more information about the **Foreach** statement, see - [about_Foreach](./About/about_Foreach.md). +The `ForEach-Object` cmdlet works much like the **Foreach** statement, except that you cannot pipe +input to a **Foreach** statement. For more information about the **Foreach** statement, see +[about_Foreach](./About/about_Foreach.md). -- Starting in PowerShell 4.0, `Where` and `ForEach` methods were added for use with collections. You - can read more about these new methods here [about_arrays](./About/about_Arrays.md) +Starting in PowerShell 4.0, `Where` and `ForEach` methods were added for use with collections. You +can read more about these new methods here [about_arrays](./About/about_Arrays.md) + +Using `ForEach-Object -Parallel`: - The `ForEach-Object -Parallel` parameter set uses PowerShell's internal API to run each script - block. This is significantly more overhead than running `ForEach-Object` normally with sequential - processing. It is important to use **Parallel** where the overhead of running in parallel is small - compared to work the script block performs. For example: + block in a new runspace. This is significantly more overhead than running `ForEach-Object` + normally with sequential processing. It is important to use **Parallel** where the overhead of + running in parallel is small compared to work the script block performs. For example: - Compute intensive scripts on multi-core machines - Scripts that spend time waiting for results or doing file operations @@ -771,8 +800,34 @@ This cmdlet returns objects that are determined by the input. the parallel scripts are trivial. Experiment with **Parallel** to discover where it can be beneficial. +- When running in parallel, objects decorated with **ScriptProperties** or **ScriptMethods** cannot + be guaranteed to function correctly if they are run in a different runspace than the scripts were + originally attached to them. + + Scriptblock invocation always attempts to run in its _home_ runspace, regardless of where it's + actually invoked. However, `ForEach-Object -Parallel` creates temporary runspaces that get deleted + after use, so there's no runspace for the scripts to execute in anymore. + + This behavior can work if as long as the _home_ runspace still exists. However, you may not get + the desired result if the script is dependent on external variables that are only present in the + caller's runspace and not the _home_ runspace. + +- Non-terminating errors are written to the cmdlet error stream as they occur in parallel running + scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in + which errors appear in the error stream is random. Likewise, messages written to other data + streams, like warning, verbose, or information are written to those data streams in an + indeterminate order. + + Terminating errors, such as exceptions, terminate the individual parallel instance of the + scriptblocks in which they occur. A terminating error in one scriptblocks may not cause the + termination of the `Foreach-Object` cmdlet. The other scriptblocks, running in parallel, continue + to run unless they also encounter a terminating error. The terminating error is written to the + error data stream as an **ErrorRecord** with a **FullyQualifiedErrorId** of `PSTaskException`. + Terminating errors can be converted to non-terminating errors using PowerShell try/catch or trap + blocks. + - [PipelineVariable](About/about_CommonParameters.md) common parameter variables are _not_ supported - in `Foreach-Object -Parallel` scenarios even with the `$using:` keyword. + in parallel scenarios even with the `$using:` keyword. > [!IMPORTANT] > The `ForEach-Object -Parallel` parameter set runs script blocks in parallel on separate process @@ -780,7 +835,7 @@ This cmdlet returns objects that are determined by the input. > thread to each running script block thread. Since the script blocks run in different threads, > the object variables passed by reference must be used safely. Generally it is safe to read from > referenced objects that don't change. But if the object state is being modified then you must - > used thread safe objects, such as .Net **System.Collection.Concurrent** types (See Example 11). + > used thread safe objects, such as .NET **System.Collection.Concurrent** types (See Example 11). ## RELATED LINKS diff --git a/reference/7.3/Microsoft.PowerShell.Core/ForEach-Object.md b/reference/7.3/Microsoft.PowerShell.Core/ForEach-Object.md index 4b9b95e943a5..bcb6b72de0ea 100644 --- a/reference/7.3/Microsoft.PowerShell.Core/ForEach-Object.md +++ b/reference/7.3/Microsoft.PowerShell.Core/ForEach-Object.md @@ -2,7 +2,7 @@ external help file: System.Management.Automation.dll-Help.xml Locale: en-US Module Name: Microsoft.PowerShell.Core -ms.date: 07/27/2021 +ms.date: 04/04/2022 online version: https://docs.microsoft.com/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.3&WT.mc_id=ps-gethelp schema: 2.0.0 title: ForEach-Object @@ -31,8 +31,8 @@ ForEach-Object [-InputObject ] [-MemberName] [-ArgumentList < ### ParallelParameterSet ``` -ForEach-Object -Parallel [-InputObject ] [-ThrottleLimit ] - [-UseNewRunspace] [-TimeoutSeconds ] [-AsJob] [-WhatIf] [-Confirm] [] +ForEach-Object [-InputObject ] -Parallel [-ThrottleLimit ] + [-TimeoutSeconds ] [-AsJob] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -84,19 +84,7 @@ command. By default, the parallel scriptblocks use the current working directory of the caller that started the parallel tasks. - Non-terminating errors are written to the cmdlet error stream as they occur in parallel running - scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in - which errors appear in the error stream is random. Likewise, messages written to other data - streams, like warning, verbose, or information are written to those data streams in an - indeterminate order. - - Terminating errors, such as exceptions, terminate the individual parallel instance of the - scriptblocks in which they occur. A terminating error in one scriptblocks may not cause the - termination of the `Foreach-Object` cmdlet. The other scriptblocks, running in parallel, continue - to run unless they also encounter a terminating error. The terminating error is written to the - error data stream as an **ErrorRecord** with a **FullyQualifiedErrorId** of `PSTaskException`. - Terminating errors can be converted to non-terminating errors using PowerShell try/catch or trap - blocks. + For more information, see the [NOTES](#notes) section of this article. ## EXAMPLES @@ -233,7 +221,7 @@ operator `$_`. It uses the dot syntax to specify the method and parentheses to e argument. The second command uses the **MemberName** parameter to specify the **Split** method and the -**ArgumentList** parameter to identify the dot (".") as the split delimiter. +**ArgumentList** parameter to identify the dot (`.`) as the split delimiter. The third command uses the **Foreach** alias of the `ForEach-Object` cmdlet and omits the names of the **MemberName** and **ArgumentList** parameters, which are optional. @@ -451,6 +439,45 @@ Output: 5 > [PipelineVariable](About/about_CommonParameters.md) common parameter variables are _not_ > supported in `Foreach-Object -Parallel` scenarios even with the `$using:` keyword. +### Example 17: Passing variables in nested parallel script ScriptBlockSet + +You can create a variable outside a `Foreach-Object -Parallel` scoped scriptblock and use +it inside the scriptblock with the `$using` keyword. + +```powershell +$test1 = 'TestA' +1..2 | Foreach-Object -Parallel { + $using:test1 +} +``` + +```Output +TestA +TestA +``` + +```powershell +# You CANNOT create a variable inside a scoped scriptblock +# to be used in a nested foreach parallel scriptblock. +$test1 = 'TestA' +1..2 | Foreach-Object -Parallel { + $using:test1 + $test2 = 'TestB' + 1..2 | Foreach-Object -Parallel { + $using:test2 + } +} +``` + +```Output +Line | + 2 | 1..2 | Foreach-Object -Parallel { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + | The value of the using variable '$using:test2' cannot be retrieved because it has not been set in the local session. +``` + +The nested scriptblock can't access the `$test2` variable and an error is thrown. + ## PARAMETERS ### -ArgumentList @@ -752,17 +779,19 @@ This cmdlet returns objects that are determined by the input. ## NOTES -- The `ForEach-Object` cmdlet works much like the **Foreach** statement, except that you cannot pipe - input to a **Foreach** statement. For more information about the **Foreach** statement, see - [about_Foreach](./About/about_Foreach.md). +The `ForEach-Object` cmdlet works much like the **Foreach** statement, except that you cannot pipe +input to a **Foreach** statement. For more information about the **Foreach** statement, see +[about_Foreach](./About/about_Foreach.md). -- Starting in PowerShell 4.0, `Where` and `ForEach` methods were added for use with collections. You - can read more about these new methods here [about_arrays](./About/about_Arrays.md) +Starting in PowerShell 4.0, `Where` and `ForEach` methods were added for use with collections. You +can read more about these new methods here [about_arrays](./About/about_Arrays.md) + +Using `ForEach-Object -Parallel`: - The `ForEach-Object -Parallel` parameter set uses PowerShell's internal API to run each script - block. This is significantly more overhead than running `ForEach-Object` normally with sequential - processing. It is important to use **Parallel** where the overhead of running in parallel is small - compared to work the script block performs. For example: + block in a new runspace. This is significantly more overhead than running `ForEach-Object` + normally with sequential processing. It is important to use **Parallel** where the overhead of + running in parallel is small compared to work the script block performs. For example: - Compute intensive scripts on multi-core machines - Scripts that spend time waiting for results or doing file operations @@ -771,8 +800,34 @@ This cmdlet returns objects that are determined by the input. the parallel scripts are trivial. Experiment with **Parallel** to discover where it can be beneficial. +- When running in parallel, objects decorated with **ScriptProperties** or **ScriptMethods** cannot + be guaranteed to function correctly if they are run in a different runspace than the scripts were + originally attached to them. + + Scriptblock invocation always attempts to run in its _home_ runspace, regardless of where it's + actually invoked. However, `ForEach-Object -Parallel` creates temporary runspaces that get deleted + after use, so there's no runspace for the scripts to execute in anymore. + + This behavior can work if as long as the _home_ runspace still exists. However, you may not get + the desired result if the script is dependent on external variables that are only present in the + caller's runspace and not the _home_ runspace. + +- Non-terminating errors are written to the cmdlet error stream as they occur in parallel running + scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in + which errors appear in the error stream is random. Likewise, messages written to other data + streams, like warning, verbose, or information are written to those data streams in an + indeterminate order. + + Terminating errors, such as exceptions, terminate the individual parallel instance of the + scriptblocks in which they occur. A terminating error in one scriptblocks may not cause the + termination of the `Foreach-Object` cmdlet. The other scriptblocks, running in parallel, continue + to run unless they also encounter a terminating error. The terminating error is written to the + error data stream as an **ErrorRecord** with a **FullyQualifiedErrorId** of `PSTaskException`. + Terminating errors can be converted to non-terminating errors using PowerShell try/catch or trap + blocks. + - [PipelineVariable](About/about_CommonParameters.md) common parameter variables are _not_ supported - in `Foreach-Object -Parallel` scenarios even with the `$using:` keyword. + in parallel scenarios even with the `$using:` keyword. > [!IMPORTANT] > The `ForEach-Object -Parallel` parameter set runs script blocks in parallel on separate process @@ -780,7 +835,7 @@ This cmdlet returns objects that are determined by the input. > thread to each running script block thread. Since the script blocks run in different threads, > the object variables passed by reference must be used safely. Generally it is safe to read from > referenced objects that don't change. But if the object state is being modified then you must - > used thread safe objects, such as .Net **System.Collection.Concurrent** types (See Example 11). + > used thread safe objects, such as .NET **System.Collection.Concurrent** types (See Example 11). ## RELATED LINKS From 44e03524b3f87e80073bd31ca5d72d4bdaa3f841 Mon Sep 17 00:00:00 2001 From: Sean Wheeler Date: Mon, 4 Apr 2022 13:46:59 -0500 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Michael T Lombardi (He/Him) --- reference/7.0/Microsoft.PowerShell.Core/ForEach-Object.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/7.0/Microsoft.PowerShell.Core/ForEach-Object.md b/reference/7.0/Microsoft.PowerShell.Core/ForEach-Object.md index 0f8550e99103..c92d5de1dc29 100644 --- a/reference/7.0/Microsoft.PowerShell.Core/ForEach-Object.md +++ b/reference/7.0/Microsoft.PowerShell.Core/ForEach-Object.md @@ -786,12 +786,12 @@ Using `ForEach-Object -Parallel`: actually invoked. However, `ForEach-Object -Parallel` creates temporary runspaces that get deleted after use, so there's no runspace for the scripts to execute in anymore. - This behavior can work if as long as the _home_ runspace still exists. However, you may not get + This behavior can work as long as the _home_ runspace still exists. However, you may not get the desired result if the script is dependent on external variables that are only present in the caller's runspace and not the _home_ runspace. - Non-terminating errors are written to the cmdlet error stream as they occur in parallel running - scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in + scriptblocks. Because parallel scriptblock execution order is non-deterministic, the order in which errors appear in the error stream is random. Likewise, messages written to other data streams, like warning, verbose, or information are written to those data streams in an indeterminate order. From cb7f8edd158e1ecce360ec51c4b99081217df9d6 Mon Sep 17 00:00:00 2001 From: Sean Wheeler Date: Mon, 4 Apr 2022 13:48:42 -0500 Subject: [PATCH 3/3] Feedback edits --- reference/7.1/Microsoft.PowerShell.Core/ForEach-Object.md | 4 ++-- reference/7.2/Microsoft.PowerShell.Core/ForEach-Object.md | 4 ++-- reference/7.3/Microsoft.PowerShell.Core/ForEach-Object.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/reference/7.1/Microsoft.PowerShell.Core/ForEach-Object.md b/reference/7.1/Microsoft.PowerShell.Core/ForEach-Object.md index c27c01285bf9..57b02c00b006 100644 --- a/reference/7.1/Microsoft.PowerShell.Core/ForEach-Object.md +++ b/reference/7.1/Microsoft.PowerShell.Core/ForEach-Object.md @@ -808,12 +808,12 @@ Using `ForEach-Object -Parallel`: actually invoked. However, `ForEach-Object -Parallel` creates temporary runspaces that get deleted after use, so there's no runspace for the scripts to execute in anymore. - This behavior can work if as long as the _home_ runspace still exists. However, you may not get + This behavior can work as long as the _home_ runspace still exists. However, you may not get the desired result if the script is dependent on external variables that are only present in the caller's runspace and not the _home_ runspace. - Non-terminating errors are written to the cmdlet error stream as they occur in parallel running - scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in + scriptblocks. Because parallel scriptblock execution order is non-deterministic, the order in which errors appear in the error stream is random. Likewise, messages written to other data streams, like warning, verbose, or information are written to those data streams in an indeterminate order. diff --git a/reference/7.2/Microsoft.PowerShell.Core/ForEach-Object.md b/reference/7.2/Microsoft.PowerShell.Core/ForEach-Object.md index bed27981dab6..2d9af09053c4 100644 --- a/reference/7.2/Microsoft.PowerShell.Core/ForEach-Object.md +++ b/reference/7.2/Microsoft.PowerShell.Core/ForEach-Object.md @@ -808,12 +808,12 @@ Using `ForEach-Object -Parallel`: actually invoked. However, `ForEach-Object -Parallel` creates temporary runspaces that get deleted after use, so there's no runspace for the scripts to execute in anymore. - This behavior can work if as long as the _home_ runspace still exists. However, you may not get + This behavior can work as long as the _home_ runspace still exists. However, you may not get the desired result if the script is dependent on external variables that are only present in the caller's runspace and not the _home_ runspace. - Non-terminating errors are written to the cmdlet error stream as they occur in parallel running - scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in + scriptblocks. Because parallel scriptblock execution order is non-deterministic, the order in which errors appear in the error stream is random. Likewise, messages written to other data streams, like warning, verbose, or information are written to those data streams in an indeterminate order. diff --git a/reference/7.3/Microsoft.PowerShell.Core/ForEach-Object.md b/reference/7.3/Microsoft.PowerShell.Core/ForEach-Object.md index bcb6b72de0ea..8b7b4d5d4826 100644 --- a/reference/7.3/Microsoft.PowerShell.Core/ForEach-Object.md +++ b/reference/7.3/Microsoft.PowerShell.Core/ForEach-Object.md @@ -808,12 +808,12 @@ Using `ForEach-Object -Parallel`: actually invoked. However, `ForEach-Object -Parallel` creates temporary runspaces that get deleted after use, so there's no runspace for the scripts to execute in anymore. - This behavior can work if as long as the _home_ runspace still exists. However, you may not get + This behavior can work as long as the _home_ runspace still exists. However, you may not get the desired result if the script is dependent on external variables that are only present in the caller's runspace and not the _home_ runspace. - Non-terminating errors are written to the cmdlet error stream as they occur in parallel running - scriptblocks. Because parallel scriptblock execution order cannot be determined, the order in + scriptblocks. Because parallel scriptblock execution order is non-deterministic, the order in which errors appear in the error stream is random. Likewise, messages written to other data streams, like warning, verbose, or information are written to those data streams in an indeterminate order.