multithreading - 如何在 Powershell 中使用 System.Threading.Thread?
问题描述
我需要将 C# 应用程序移植到 Powershell。该应用程序使用线程来完成繁重的工作,我需要使应用程序的端口尽可能接近原始 C# 应用程序。问题是我无法使用System.Threading.Thread生成新线程,因为System.Threading.ThreadStart在 Powershell中要求2 个参数,而 C# 代码仅在代码中要求1,因为它是委托。
这是 C# 中生成线程的最小类示例
using System;
using System.Threading;
namespace ThreadingTest
{
class ThreadingTest
{
public delegate void UpdateElementCallback(string message);
public ThreadingTest() {
Console.WriteLine("Hello from Thread " + Thread.CurrentThread.ManagedThreadId);
Thread newThread = new Thread(new ThreadStart(MethodToBeCalledFromAnotherThread));
newThread.Start();
}
private void UpdateElement(string message) {
Console.WriteLine(message + Thread.CurrentThread.ManagedThreadId);
}
private void MethodToBeCalledFromAnotherThread() {
UpdateElement("Hello from Thread ");
}
}
}
示例运行中的哪些输出
Hello from Thread 1
Hello from Thread 4
这是我尝试过的 Powershell 代码的非工作端口
Add-Type -AssemblyName System
Add-Type -AssemblyName System.Threading
class ThreadingTest {
[Action[String]]$UpdateElementCallback =
{
param([String]$message)
$callBackThread = [System.Threading.Thread]::CurrentThread.ManagedThreadId
"$message $callBackThread" | Out-Host
}
# Constructor
ThreadingTest() {
# Get current thread id
$currentThread = [System.Threading.Thread]::CurrentThread.ManagedThreadId
# Print current thread id
"Hello from Thread $currentThread" | Out-Host
# Create a new thread (This line fails)
$newThread = [System.Threading.Thread]::new([System.Threading.ThreadStart]::new($this.UpdateElementCallback))
$newThread.Start
}
}
$ThreadingTestObject = [ThreadingTest]::new()
示例运行中的哪些输出
Hello from Thread 21
MethodException: .../ThreadingTest.ps1:21:9
Line |
21 | $newThread = [System.Threading.Thread]::new([System.Threading …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Cannot find an overload for "new" and the argument count: "1".
用于创建新ThreadingStart对象的 Powershell 调用具有以下签名
System.Threading.ThreadStart new(System.Object object, System.IntPtr method)
我必须传递什么作为参数才能使其工作?System.Object必须是什么,System.IntPtr必须是什么作为参数传递才能使其工作?
我想说明我需要使代码尽可能接近原始代码。我尝试了 Start-Job 和 Runspaces。运行空间工作,但它需要大量的样板。我想知道是否有办法通过使用System.Threading.Thread使其工作
解决方案
找到的解决方法是使用 C# 在此处创建System.Threading.Thread对象并将其传递给 Powershell。然后像往常一样使用 Powershell 中的线程对象。
从 Powershell 调用 C# 并使用 System.Threading.Thread
注意:此代码仅适用于Powershell 7。以前的版本会抛出一个错误,即它们无法将类中的方法转换为 Action。
$code = @"
using System;
using System.Threading;
namespace ThreadingTest
{
public class ThreadCreator
{
public Thread CreateAThread(ThreadStart threadStart) {
return new Thread(threadStart);
}
public ThreadStart CreateAThreadStart(Action threadStart) {
return new ThreadStart(threadStart);
}
}
}
"@
Add-Type -TypeDefinition $code -Language CSharp
class ThreadingTestClass {
$ThreadCreator
[System.Threading.Thread] $NewThread
[bool] $finishCountingThread = $false
[int] $ClassThreadId
# Constructor
ThreadingTestClass() {
# Get the current thread id
$this.ClassThreadId = [System.Threading.Thread]::CurrentThread.ManagedThreadId
# Print current thread id
Write-Host "Hello from " -NoNewline
Write-Host "Thread $($this.ClassThreadId)" -ForegroundColor Green
# Create a ThreadCreator object
$this.ThreadCreator = Invoke-Expression "[ThreadingTest.ThreadCreator]::new()"
# Get a [System.Threading.Thread] object from the C# code to run '[Void] MethodToBeCalledFromAnotherThread()' in another thread
$this.NewThread = $this.ThreadCreator.CreateAThread($this.ThreadCreator.CreateAThreadStart($this.MethodToBeCalledFromAnotherThread))
# Start the thread
$this.NewThread.Start()
# Wait for user to press Escape. This 'do until' is executed while the other thread is running in the background. It doesn't wait for $this.NewThread to finish
do {
$keyPressed = [System.Console]::ReadKey().Key
} until ($keyPressed -eq [System.ConsoleKey]::Escape)
# Set the $finishCountingThread variable to stop counting
$this.finishCountingThread = $true;
# Wait for the thread to finish gracefully before ending
$this.NewThread.Join()
}
[Void] MethodToBeCalledFromAnotherThread() {
# Get the current thread id
$currentThreadID = [System.Threading.Thread]::CurrentThread.ManagedThreadId
# Say hello from the thread this method is running on
Write-Host "Hello from " -NoNewline
Write-Host "Thread $currentThreadId" -ForegroundColor Blue
# Count from 0 to 10000
for ($i = 0; $i -le 10000; $i++) {
# Check boolean to know whether to finish counting or not
if($this.finishCountingThread) {
Write-Host "`nFinishing spawned " -NoNewline
Write-Host "Thread $currentThreadID..." -ForegroundColor Blue
break
}
# Print on the console the current value of $i
Write-Host "`rPress ESC to stop spawned " -NoNewline
Write-Host "Thread $currentThreadID " -NoNewline -ForegroundColor Blue
Write-Host "and quit program: " -NoNewline
Write-Host "[Counting $i in Thread $currentThreadID] " –NoNewline -ForegroundColor Blue
Write-Host "Keys are being listened to on " -NoNewline
Write-Host "Thread $($this.ClassThreadId)" -NoNewline -ForegroundColor Green
}
# Check if the counting finished before the user pressed Escape
if (-Not $this.finishCountingThread) {
"`nCounting finished" | Out-Host
"Press ESC to quit program..." | Out-Host
}
}
}
# Set cursor visible to false to see colors without a jittering cursor
[System.Console]::CursorVisible = $false
"`n`n" | Out-Host
$ThreadingTestClassObject = [ThreadingTestClass]::new()
"`n`n" | Out-Host
该代码调用 C# 代码来创建一个对象,该对象具有创建线程的方法。调用其中两个方法来创建System.Threading.Thread 对象并返回它。然后可以从 Powershell 使用线程对象。因为传递给衍生线程 ( MethodToBeCalledFromAnotherThread ) 的方法在 Powershell 类中,所以即使它在另一个线程中,它也可以访问该类的属性。
替代方案 - 使用 System.ComponentModel.BackgroundWorker
以下内容没有回答如何在 Powershell 中使用System.Threading.Thread的问题,但它是不调用任何 C# 代码且不使用运行空间或作业的多线程的另一种解决方案。它使用 BackgroundWorker,如下所示。当使用表单或 WPF GUI 时,它很有用。输出与其他代码中的相同。
注意:此代码仅适用于Powershell 7。以前的版本会抛出一个错误,即它们无法将类中的方法转换为 Action。
class ThreadingTestClass {
[System.ComponentModel.BackgroundWorker] $BackgroundWorker
[bool] $finishCountingThread = $false
[int] $ClassThreadId
# Constructor
ThreadingTestClass() {
# Get the current thread id
$this.ClassThreadId = [System.Threading.Thread]::CurrentThread.ManagedThreadId
# Print current thread id
Write-Host "Hello from " -NoNewline
Write-Host "Thread $($this.ClassThreadId)" -ForegroundColor Green
# Create a BackgroundWorker object, set the event and set it to support cancellation
$this.BackgroundWorker = [System.ComponentModel.BackgroundWorker]::new()
$this.BackgroundWorker.add_DoWork($this.MethodToBeCalledFromAnotherThread)
$this.BackgroundWorker.WorkerSupportsCancellation = $true
# Run the method 'MethodToBeCalledFromAnotherThread' in another thread
$this.BackgroundWorker.RunWorkerAsync()
# Wait for user to press Escape. This 'do until' is executed while the other thread is running in the background. It doesn't wait for $this.BackgroundWorker to finish
do {
$keyPressed = [System.Console]::ReadKey().Key
} until ($keyPressed -eq [System.ConsoleKey]::Escape)
# Set the $finishCountingThread variable to stop counting
$this.finishCountingThread = $true
if ($this.BackgroundWorker.WorkerSupportsCancellation -and $this.BackgroundWorker.IsBusy) {
# Cancel the background worker work
$this.BackgroundWorker.CancelAsync()
# while loop only needed if there is no GUI or anything else preventing PS from continuing and trying to exit
while ($this.BackgroundWorker.IsBusy) {
# Do nothing. Block calling thread and wait for the Background worker to finish cancelling
# Otherwise, Powershell will continue to execute code further and it can
# end code execution while there is another thread in the background
# That will cause an exception and a crash
# GUIs usually prevent PowerShell from exiting
# for which this may not be needed in a Form or WPF application
}
}
}
[Void] MethodToBeCalledFromAnotherThread([Object]$sender, [System.ComponentModel.DoWorkEventArgs]$e) {
# Get the current thread id
$currentThreadID = [System.Threading.Thread]::CurrentThread.ManagedThreadId
# Say hello from the thread this method is running on
Write-Host "Hello from " -NoNewline
Write-Host "Thread $currentThreadId" -ForegroundColor Blue
# Count from 0 to 10000
for ($i = 0; $i -le 10000; $i++) {
# Check boolean to know whether to finish counting or not
if($this.finishCountingThread) {
Write-Host "`nFinishing spawned " -NoNewline
Write-Host "Thread $currentThreadID..." -ForegroundColor Blue
break
}
# Print on the console the current value of $i
Write-Host "`rPress ESC to stop spawned " -NoNewline
Write-Host "Thread $currentThreadID " -NoNewline -ForegroundColor Blue
Write-Host "and quit program: " -NoNewline
Write-Host "[Counting $i in Thread $currentThreadID] " –NoNewline -ForegroundColor Blue
Write-Host "Keys are being listened to on " -NoNewline
Write-Host "Thread $($this.ClassThreadId)" -NoNewline -ForegroundColor Green
}
# Check if the counting finished before the user pressed Escape
if (-Not $this.finishCountingThread) {
"`nCounting finished" | Out-Host
"Press ESC to quit program..." | Out-Host
}
}
}
# Set cursor visible to false to see colors without a jittering cursor
[System.Console]::CursorVisible = $false
"`n`n" | Out-Host
$ThreadingTestClassObject = [ThreadingTestClass]::new()
"`n`n" | Out-Host
推荐阅读
- postgresql - INSERT 按顺序而不是 UPDATE ... WHERE
- servicestack - QueryString.Add() 给出“不支持指定的方法”。
- set - 如何从具有给定多重集的集中减去多重集?
- python - IndexError:手动创建火花数据框时列表索引超出范围?
- node.js - Apple 的 MusicKit JS 库示例在由 Node.js 呈现时运行良好,但 Django 失败
- android - 为片段充气时创建“滑动”动画
- java - DDMS 无法在 Mac OS 上启动
- c++ - C++ 把一个词换成另一个词
- c# - Regex.Split(多个空格)
- python - Python:从 A 类中的 B 类调用函数返回 AttributeError