【问题标题】:Action delay extension method not working动作延迟扩展方法不起作用
【发布时间】:2011-10-17 16:56:04
【问题描述】:

我正在尝试为 Action 创建一个方便的扩展方法,以便在延迟后基本上运行该操作:到目前为止,我的扩展看起来像这样

    public static void DelayAction(this Action DelayedAction, int millisecondDelay, CancellationToken Token)
    {
        Task t = Task.Factory.StartNew(() => { Thread.Sleep(millisecondDelay); }, Token, TaskCreationOptions.None, TaskScheduler.Default);            
        t.ContinueWith(_ => DelayedAction, Token, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
    }

我有一个我调用的函数,它反过来使用这些扩展

    private void DelayTask(Action ActiontoDelay, int millisecondDelay)
    {
        ActiontoDelay.DelayAction(millisecondDelay, _TokenSource.Token);
    } 

我这样称呼:

    DelayTask(() => { _SomeFunction(SomeArgs); }, 1500);

但这一切似乎都是一个整体,动作永远不会触发。我哪里错了?

编辑 17-11-11 2300hrs:

我删除了通用扩展方法,因为它与本示例无关。

还在这里发表评论,因为它没有在 cmets 中清楚地格式化代码

如果不是调用

DelayTask(() => { _SomeFunction(SomeArgs); }, 1500); 

我直接这样做:

Task t = Task.Factory.StartNew(() => { Thread.Sleep(1500); }, Token, TaskCreationOptions.None, TaskScheduler.Default); 
t.ContinueWith(() => { _SomeFunction(SomeArgs); }, Token, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); 

它工作正常(对不起,如果那里有任何语法错误,我已经从记忆中完成了)所以我相信我的问题在于处理尼克巴特勒的答案所逃避的动作

【问题讨论】:

  • 你没有给我们任何关于你正在运行什么样的应用程序等方面的背景信息。你能举一个简短但完整的例子来说明这个问题吗?见tinyurl.com/so-hints
  • 要调用 Action<T> 你需要一个 T 的实例作为参数传递,我在你的代码 sn-ps 中看不到它
  • 你确定你有SynchronizationContext吗?
  • 抱歉,Action 只是混淆了问题,所以我将其删除。问题是如何在不使用计时器的情况下延迟操作(我知道我可以使用但我试图保持一致并尽可能使用任务)
  • @JonSkeet - 此实例中的上下文是建立在 VB 6 事件模型之上的状态机,为了响应此模型引发的某些事件,我希望将下一个操作推迟一段时间时间。本质上,我只是在尝试使用任务来模拟计时器回调延迟,我想我可以通过扩展 Action 类型来做到这一点。

标签: c# multithreading extension-methods action task


【解决方案1】:

您的延续正在返回 DelayedAction 委托,而不是调用它:

t.ContinueWith(_ => DelayedAction(), Token, ...

【讨论】:

  • 我们赢了!而且我忘记了括号是个失败者:)
【解决方案2】:

如果 Tasks 不是一个强烈的要求,我建议使用 System.Threading.Timer 内置“延迟”功能,构造函数如下所示:

public Timer(
    TimerCallback callback,
    Object state,
    int dueTime,
    int period
)

MSDN:

dueTime 类型:System.Int32 调用回调之前的延迟时间,以毫秒为单位。将 Timeout.Infinite 指定为 防止定时器启动。指定零 (0) 以启动计时器 马上。

还有一点很重要,您可以在施工阶段之后更改此延迟。


public static void DelayAction<T>(
            this Action<T> delayedAction, 
            T argument, 
            int millisecondDelay, 
            CancellationToken cancellationToken)
        {
            // Timeout.Infinite to disable periodic signaling.
            var timer = new System.Threading.Timer(x =>
                                {
                                   cancellationToken.ThrowIfCancellationRequested();
                                   delayedAction.Invoke(argument);
                                }, 
                                null, 
                                millisecondDelay, 
                                Timeout.Infinite);                        
        }  

【讨论】:

  • 我喜欢它,简单而且有效,虽然我没有实现,但我会投赞成票。
【解决方案3】:

当然,您可以通过任务来做到这一点。问题是,您调用了 TaskFactory 的 StartNew,它会立即启动您的任务。

以这种方式尝试 - 这应该可以:

public static void DelayAction<T>(this Action<T> DelayedAction, int millisecondDelay, CancellationToken Token)
{
    var task = new Task(t => { Thread.Sleep(millisecondDelay); }, null, Token);
    task.ContinueWith(t => DelayedAction, Token, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
    task.Start();
} 

【讨论】:

  • 根据我的理解和经验,StartNew 实际上是向队列发布消息,并且不会立即启动。如果我错了并且不幸的巧合到目前为止很幸运,请告诉我!
猜你喜欢
  • 2016-12-12
  • 2020-11-11
  • 2013-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多