System.Threading.Timer 杀死 PowerShell 控制台

Posted

技术标签:

【中文标题】System.Threading.Timer 杀死 PowerShell 控制台【英文标题】:System.Threading.Timer kills the PowerShell console 【发布时间】:2018-12-14 23:37:24 【问题描述】:

当我在 PowerShell 控制台中运行它时:

$callback = [System.Threading.TimerCallback]
    param($state)


$timer = [System.Threading.Timer]::new($callback, $null,
    [timespan]::Zero,
    [timespan]::FromSeconds(1))

然后一旦$callback 被调用(我确定这是根本原因,通过将dueTime constructor's parameter 从[timespan]::Zero 更改为更长的延迟),整个控制台进程崩溃,说powershell has stopped working

可能是什么原因?我该如何克服呢?

【问题讨论】:

【参考方案1】:

错误是:

在此线程中没有可用于运行脚本的运行空间。你可以 在 DefaultRunspace 属性中提供一个 System.Management.Automation.Runspaces.Runspace 类型。

而源于

System.Management.Automation.ScriptBlock.GetContextFromTLS()

委托是问题所在。这就是我的工作方式:

$helper = @'
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Management.Automation.Runspaces;

public class RunspacedDelegateFactory

    public static Delegate NewRunspacedDelegate(Delegate _delegate, Runspace runspace)
    
        Action setRunspace = () => Runspace.DefaultRunspace = runspace;

        return ConcatActionToDelegate(setRunspace, _delegate);
    

    private static Expression ExpressionInvoke(Delegate _delegate, params Expression[] arguments)
    
        var invokeMethod = _delegate.GetType().GetMethod("Invoke");

        return Expression.Call(Expression.Constant(_delegate), invokeMethod, arguments);
    

    public static Delegate ConcatActionToDelegate(Action a, Delegate d)
    
        var parameters =
            d.GetType().GetMethod("Invoke").GetParameters()
            .Select(p => Expression.Parameter(p.ParameterType, p.Name))
            .ToArray();

        Expression body = Expression.Block(ExpressionInvoke(a), ExpressionInvoke(d, parameters));

        var lambda = Expression.Lambda(d.GetType(), body, parameters);

        var compiled = lambda.Compile();

        return compiled;
    

'@

add-type -TypeDefinition $helper

$runspacedDelegate = [RunspacedDelegateFactory]::NewRunspacedDelegate($callback, [Runspace]::DefaultRunspace)

$timer = [System.Threading.Timer]::new(
    $runspacedDelegate,
    $null,
    [timespan]::Zero,
    [timespan]::FromSeconds(1))

我借用了 NewRunspacedDelegate

https://www.powershellgallery.com/packages/ContainerTools/0.0.1

【讨论】:

哇,大惊小怪……令人印象深刻,但不确定是否值得……为什么要这么难,只是为了派代表? 顺便说一句,你是如何设法得到这个错误的?控制台只是为我崩溃了。 记录在事件日志中。也就是说,全文错误是从 PowerShell 6 输出的。

以上是关于System.Threading.Timer 杀死 PowerShell 控制台的主要内容,如果未能解决你的问题,请参考以下文章

为啥 System.Timers.Timer 能在 GC 中存活,而 System.Threading.Timer 不能?

System.Windows.Forms.TimerSystem.Timers.TimerSystem.Threading.Timer

如何在 C# 中创建计时器而不使用 System.Timers.Timer 或 System.Threading.Timer

System.Threading.Timer:它为啥讨厌我?

System.Threading.Timer 在 25-30 次后停止 [重复]

如何处理重复 System.Threading.Timer [重复]