| 
                        
                             | 
                    气势凌人的稀饭 · 余渔:心怀家国,建设边疆· 2 月前 · | 
| 
                        
                             | 
                    强悍的柚子 · 宋向前:切莫浪费了 ...· 2 年前 · | 
| 
                        
                             | 
                    逃跑的生菜 · 丰田为什么不给雷克萨斯ES,配上2.0T发动 ...· 2 年前 · | 
我需要将一个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 必须是什么才能工作?
我想做一个注意,我需要保持代码尽可能接近原。我试过开始-工作和运行空间。运行空间有效,但它需要大量的样板。我想知道是否有一种通过使用 System.Threading.Thread 使其工作的方法
发布于 2020-06-06 23:30:35
找到的解决方法是使用C#在那里创建一个C#对象并将其传递给Powershell。然后将Powershell中的线程对象作为常规使用。
从Powershell调用C#并使用System.Threading.Thread
Notes :此代码仅适用于 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
            $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类中,所以即使它在另一个线程中,也可以访问该类的属性。
当它运行到结束时输出:
                                          
                                         
                                        
                                       
                                      
                                     
                                    
                                   
                                  
                                 
                                
                               
                              
                             
                            
                           当按下 ESC 以停止计数时输出
                                          
                                         
                                        
                                       
                                      
                                     
                                    
                                   
                                  
                                 
                                
                               
                              
                             
                            
                           
替代-使用System.ComponentModel.BackgroundWorker
下面没有回答如何在Powershell中使用 System.Threading.Thread 的问题,但它是另一种不调用任何C#代码、不使用运行空间或乔布斯的多线程解决方案。它使用BackgroundWorker,如下所示。当使用表单或WPF GUI时,它非常有用。输出与其他代码中的输出相同。
Notes :此代码仅适用于 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
            $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
                        | 
                         | 
                    气势凌人的稀饭 · 余渔:心怀家国,建设边疆 2 月前 |